Merge "Change drag to edge to requestSplit when not in freeform." into main
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index b42f7bc..e857175 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,6 +1,7 @@
 [Builtin Hooks]
 clang_format = true
 bpfmt = true
+ktfmt = true
 
 [Builtin Hooks Options]
 # Only turn on clang-format check for the following subfolders.
@@ -17,6 +18,7 @@
                tests/
                tools/
 bpfmt = -d
+ktfmt = --kotlinlang-style --include-dirs=services/permission,packages/SystemUI
 
 [Hook Scripts]
 checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
@@ -25,9 +27,10 @@
 
 hidden_api_txt_exclude_hook = ${REPO_ROOT}/frameworks/base/tools/hiddenapi/exclude.sh ${PREUPLOAD_COMMIT} ${REPO_ROOT}
 
-ktfmt_hook = ${REPO_ROOT}/external/ktfmt/ktfmt.py --check -i ${REPO_ROOT}/frameworks/base/ktfmt_includes.txt ${PREUPLOAD_FILES}
-
 ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py --no-verify-format -f ${PREUPLOAD_FILES}
 
 # This flag check hook runs only for "packages/SystemUI" subdirectory. If you want to include this check for other subdirectories, please modify flag_check.py.
 flag_hook = ${REPO_ROOT}/frameworks/base/packages/SystemUI/flag_check.py --msg=${PREUPLOAD_COMMIT_MESSAGE} --files=${PREUPLOAD_FILES} --project=${REPO_PATH}
+
+[Tool Paths]
+ktfmt = ${REPO_ROOT}/prebuilts/build-tools/common/framework/ktfmt.jar
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
index 459c286..515ddc8 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
@@ -19,16 +19,13 @@
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.perftests.utils.ShellHelper;
-import android.util.Log;
 
 import java.util.ArrayList;
 
 // Based on //platform/frameworks/base/apct-tests/perftests/utils/BenchmarkState.java
 public class BenchmarkRunner {
-    private static final String TAG = BenchmarkRunner.class.getSimpleName();
+
     private static final long COOL_OFF_PERIOD_MS = 1000;
-    private static final int CPU_IDLE_TIMEOUT_MS = 60 * 1000;
-    private static final int CPU_IDLE_THRESHOLD_PERCENTAGE = 90;
 
     private static final int NUM_ITERATIONS = 4;
 
@@ -83,7 +80,7 @@
 
     private void prepareForNextRun() {
         SystemClock.sleep(COOL_OFF_PERIOD_MS);
-        waitCoolDownPeriod();
+        ShellHelper.runShellCommand("am wait-for-broadcast-idle --flush-broadcast-loopers");
         mStartTimeNs = System.nanoTime();
         mPausedDurationNs = 0;
     }
@@ -105,7 +102,7 @@
      * to avoid unnecessary waiting.
      */
     public void resumeTiming() {
-        waitCoolDownPeriod();
+        ShellHelper.runShellCommand("am wait-for-broadcast-idle --flush-broadcast-loopers");
         resumeTimer();
     }
 
@@ -165,56 +162,4 @@
         }
         return null;
     }
-
-    /** Waits for the broadcast queue and the CPU cores to be idle. */
-    public void waitCoolDownPeriod() {
-        waitForBroadcastIdle();
-        waitForCpuIdle();
-    }
-
-    private void waitForBroadcastIdle() {
-        Log.d(TAG, "starting to waitForBroadcastIdle");
-        final long startedAt = System.currentTimeMillis();
-        ShellHelper.runShellCommand("am wait-for-broadcast-idle --flush-broadcast-loopers");
-        final long elapsed = System.currentTimeMillis() - startedAt;
-        Log.d(TAG, "waitForBroadcastIdle is complete in " + elapsed + " ms");
-    }
-    private void waitForCpuIdle() {
-        Log.d(TAG, "starting to waitForCpuIdle");
-        final long startedAt = System.currentTimeMillis();
-        while (true) {
-            final int idleCpuPercentage = getIdleCpuPercentage();
-            final long elapsed = System.currentTimeMillis() - startedAt;
-            Log.d(TAG, "waitForCpuIdle " + idleCpuPercentage + "% (" + elapsed + "ms elapsed)");
-            if (idleCpuPercentage >= CPU_IDLE_THRESHOLD_PERCENTAGE) {
-                Log.d(TAG, "waitForCpuIdle is complete in " + elapsed + " ms");
-                return;
-            }
-            if (elapsed >= CPU_IDLE_TIMEOUT_MS) {
-                Log.e(TAG, "Ending waitForCpuIdle because it didn't finish in "
-                        + CPU_IDLE_TIMEOUT_MS + " ms");
-                return;
-            }
-            SystemClock.sleep(1000);
-        }
-    }
-
-    private int getIdleCpuPercentage() {
-        String output = ShellHelper.runShellCommand("top -m 1 -n 1");
-        String[] tokens = output.split("\\s+");
-        float totalCpu = -1;
-        float idleCpu = -1;
-        for (String token : tokens) {
-            if (token.contains("%cpu")) {
-                totalCpu = Float.parseFloat(token.split("%")[0]);
-            } else if (token.contains("%idle")) {
-                idleCpu = Float.parseFloat(token.split("%")[0]);
-            }
-        }
-        if (totalCpu < 0 || idleCpu < 0) {
-            Log.e(TAG, "Could not get idle cpu percentage, output=" + output);
-            return -1;
-        }
-        return (int) (100 * idleCpu / totalCpu);
-    }
 }
\ No newline at end of file
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 98ab0c2..762e2af 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -188,6 +188,21 @@
         }
     }
 
+    /** Tests creating a new user, with wait times between iterations. */
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+    public void createUser_realistic() throws RemoteException {
+        while (mRunner.keepRunning()) {
+            Log.i(TAG, "Starting timer");
+            final int userId = createUserNoFlags();
+
+            mRunner.pauseTiming();
+            Log.i(TAG, "Stopping timer");
+            removeUser(userId);
+            waitCoolDownPeriod();
+            mRunner.resumeTimingForNextIteration();
+        }
+    }
+
     /** Tests creating and starting a new user. */
     @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void createAndStartUser() throws RemoteException {
@@ -224,6 +239,7 @@
             mRunner.pauseTiming();
             Log.d(TAG, "Stopping timer");
             removeUser(userId);
+            waitCoolDownPeriod();
             mRunner.resumeTimingForNextIteration();
         }
     }
@@ -238,6 +254,7 @@
             mRunner.pauseTiming();
             final int userId = createUserNoFlags();
 
+            waitForBroadcastIdle();
             runThenWaitForBroadcasts(userId, () -> {
                 mRunner.resumeTiming();
                 Log.i(TAG, "Starting timer");
@@ -292,6 +309,9 @@
 
             preStartUser(userId, numberOfIterationsToSkip);
 
+            waitForBroadcastIdle();
+            waitCoolDownPeriod();
+
             runThenWaitForBroadcasts(userId, () -> {
                 mRunner.resumeTiming();
                 Log.i(TAG, "Starting timer");
@@ -333,6 +353,9 @@
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
 
+            waitForBroadcastIdle();
+            waitCoolDownPeriod();
+
             runThenWaitForBroadcasts(userId, () -> {
                 mRunner.resumeTiming();
                 Log.i(TAG, "Starting timer");
@@ -397,6 +420,7 @@
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
 
+            waitCoolDownPeriod();
             mRunner.resumeTiming();
             Log.i(TAG, "Starting timer");
 
@@ -430,6 +454,7 @@
             mRunner.pauseTiming();
             Log.d(TAG, "Stopping timer");
             removeUser(userId);
+            waitCoolDownPeriod();
             mRunner.resumeTimingForNextIteration();
         }
     }
@@ -441,7 +466,6 @@
             mRunner.pauseTiming();
             final int startUser = mAm.getCurrentUser();
             final int userId = createUserNoFlags();
-
             mRunner.resumeTiming();
             Log.i(TAG, "Starting timer");
 
@@ -455,6 +479,27 @@
         }
     }
 
+    /** Tests switching to an uninitialized user with wait times between iterations. */
+    @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+    public void switchUser_realistic() throws Exception {
+        while (mRunner.keepRunning()) {
+            mRunner.pauseTiming();
+            final int startUser = ActivityManager.getCurrentUser();
+            final int userId = createUserNoFlags();
+            waitCoolDownPeriod();
+            Log.d(TAG, "Starting timer");
+            mRunner.resumeTiming();
+
+            switchUser(userId);
+
+            mRunner.pauseTiming();
+            Log.d(TAG, "Stopping timer");
+            switchUserNoCheck(startUser);
+            removeUser(userId);
+            mRunner.resumeTimingForNextIteration();
+        }
+    }
+
     /** Tests switching to a previously-started, but no-longer-running, user. */
     @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void switchUser_stopped() throws RemoteException {
@@ -462,7 +507,6 @@
             mRunner.pauseTiming();
             final int startUser = mAm.getCurrentUser();
             final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ true);
-
             mRunner.resumeTiming();
             Log.i(TAG, "Starting timer");
 
@@ -492,6 +536,7 @@
 
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
+            waitCoolDownPeriod();
             Log.d(TAG, "Starting timer");
             mRunner.resumeTiming();
 
@@ -517,6 +562,7 @@
                 /* useStaticWallpaper */true);
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
+            waitCoolDownPeriod();
             Log.d(TAG, "Starting timer");
             mRunner.resumeTiming();
 
@@ -560,6 +606,7 @@
         final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ false);
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
+            waitCoolDownPeriod();
             Log.d(TAG, "Starting timer");
             mRunner.resumeTiming();
 
@@ -567,6 +614,7 @@
 
             mRunner.pauseTiming();
             Log.d(TAG, "Stopping timer");
+            waitForBroadcastIdle();
             switchUserNoCheck(startUser);
             mRunner.resumeTimingForNextIteration();
         }
@@ -583,6 +631,7 @@
                 /* useStaticWallpaper */ true);
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
+            waitCoolDownPeriod();
             Log.d(TAG, "Starting timer");
             mRunner.resumeTiming();
 
@@ -590,6 +639,7 @@
 
             mRunner.pauseTiming();
             Log.d(TAG, "Stopping timer");
+            waitForBroadcastIdle();
             switchUserNoCheck(startUser);
             mRunner.resumeTimingForNextIteration();
         }
@@ -625,11 +675,13 @@
     @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
     public void stopUser_realistic() throws RemoteException {
         final int userId = createUserNoFlags();
+        waitCoolDownPeriod();
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
             runThenWaitForBroadcasts(userId, ()-> {
                 mIam.startUserInBackground(userId);
             }, Intent.ACTION_USER_STARTED, Intent.ACTION_MEDIA_MOUNTED);
+            waitCoolDownPeriod();
             Log.d(TAG, "Starting timer");
             mRunner.resumeTiming();
 
@@ -651,7 +703,7 @@
             final int startUser = mAm.getCurrentUser();
             final int userId = createUserNoFlags();
 
-            mRunner.waitCoolDownPeriod();
+            waitForBroadcastIdle();
             mUserSwitchWaiter.runThenWaitUntilBootCompleted(userId, () -> {
                 mRunner.resumeTiming();
                 Log.i(TAG, "Starting timer");
@@ -674,7 +726,7 @@
             final int startUser = ActivityManager.getCurrentUser();
             final int userId = createUserNoFlags();
 
-            mRunner.waitCoolDownPeriod();
+            waitCoolDownPeriod();
             mUserSwitchWaiter.runThenWaitUntilBootCompleted(userId, () -> {
                 mRunner.resumeTiming();
                 Log.d(TAG, "Starting timer");
@@ -700,7 +752,7 @@
                 switchUser(userId);
             }, Intent.ACTION_MEDIA_MOUNTED);
 
-            mRunner.waitCoolDownPeriod();
+            waitForBroadcastIdle();
             mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> {
                 runThenWaitForBroadcasts(userId, () -> {
                     mRunner.resumeTiming();
@@ -729,7 +781,7 @@
                 switchUser(userId);
             }, Intent.ACTION_MEDIA_MOUNTED);
 
-            mRunner.waitCoolDownPeriod();
+            waitCoolDownPeriod();
             mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> {
                 runThenWaitForBroadcasts(userId, () -> {
                     mRunner.resumeTiming();
@@ -775,6 +827,7 @@
             Log.d(TAG, "Stopping timer");
             attestTrue("Failed creating profile " + userId, mUm.isManagedProfile(userId));
             removeUser(userId);
+            waitCoolDownPeriod();
             mRunner.resumeTimingForNextIteration();
         }
     }
@@ -815,6 +868,7 @@
             mRunner.pauseTiming();
             Log.d(TAG, "Stopping timer");
             removeUser(userId);
+            waitCoolDownPeriod();
             mRunner.resumeTimingForNextIteration();
         }
     }
@@ -859,6 +913,7 @@
 
             mRunner.pauseTiming();
             Log.d(TAG, "Stopping timer");
+            waitCoolDownPeriod();
             mRunner.resumeTimingForNextIteration();
         }
         removeUser(userId);
@@ -910,6 +965,7 @@
             mRunner.pauseTiming();
             Log.d(TAG, "Stopping timer");
             removeUser(userId);
+            waitCoolDownPeriod();
             mRunner.resumeTimingForNextIteration();
         }
     }
@@ -974,6 +1030,7 @@
             mRunner.pauseTiming();
             Log.d(TAG, "Stopping timer");
             removeUser(userId);
+            waitCoolDownPeriod();
             mRunner.resumeTimingForNextIteration();
         }
     }
@@ -1014,6 +1071,7 @@
             mRunner.pauseTiming();
             Log.d(TAG, "Stopping timer");
             removeUser(userId);
+            waitCoolDownPeriod();
             mRunner.resumeTimingForNextIteration();
         }
     }
@@ -1066,6 +1124,7 @@
             mRunner.pauseTiming();
             Log.d(TAG, "Stopping timer");
             removeUser(userId);
+            waitCoolDownPeriod();
             mRunner.resumeTimingForNextIteration();
         }
     }
@@ -1105,6 +1164,7 @@
             runThenWaitForBroadcasts(userId, () -> {
                 startUserInBackgroundAndWaitForUnlock(userId);
             }, Intent.ACTION_MEDIA_MOUNTED);
+            waitCoolDownPeriod();
             mRunner.resumeTiming();
             Log.d(TAG, "Starting timer");
 
@@ -1220,7 +1280,6 @@
      * If lack of success should fail the test, use {@link #switchUser(int)} instead.
      */
     private boolean switchUserNoCheck(int userId) throws RemoteException {
-        mRunner.waitCoolDownPeriod();
         final boolean[] success = {true};
         mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(userId, () -> {
             mAm.switchUser(userId);
@@ -1237,7 +1296,7 @@
      */
     private void stopUserAfterWaitingForBroadcastIdle(int userId)
             throws RemoteException {
-        mRunner.waitCoolDownPeriod();
+        waitForBroadcastIdle();
         stopUser(userId);
     }
 
@@ -1379,8 +1438,6 @@
      */
     private void runThenWaitForBroadcasts(int userId, FunctionalUtils.ThrowingRunnable runnable,
             String... actions) {
-        mRunner.waitCoolDownPeriod();
-
         final String unreceivedAction =
                 mBroadcastWaiter.runThenWaitForBroadcasts(userId, runnable, actions);
 
@@ -1481,4 +1538,28 @@
         assertEquals("", ShellHelper.runShellCommand("setprop " + name + " " + value));
         return TextUtils.firstNotEmpty(oldValue, "invalid");
     }
+
+    private void waitForBroadcastIdle() {
+        try {
+            ShellHelper.runShellCommandWithTimeout(
+                    "am wait-for-broadcast-idle --flush-broadcast-loopers", TIMEOUT_IN_SECOND);
+        } catch (TimeoutException e) {
+            Log.e(TAG, "Ending waitForBroadcastIdle because it is taking too long", e);
+        }
+    }
+
+    private void sleep(long ms) {
+        try {
+            Thread.sleep(ms);
+        } catch (InterruptedException e) {
+            // Ignore
+        }
+    }
+
+    private void waitCoolDownPeriod() {
+        // Heuristic value based on local tests. Stability increased compared to no waiting.
+        final int tenSeconds = 1000 * 10;
+        waitForBroadcastIdle();
+        sleep(tenSeconds);
+    }
 }
diff --git a/apct-tests/perftests/permission/src/android/perftests/permission/PermissionManagerPerfTest.kt b/apct-tests/perftests/permission/src/android/perftests/permission/PermissionManagerPerfTest.kt
new file mode 100644
index 0000000..8b8036e
--- /dev/null
+++ b/apct-tests/perftests/permission/src/android/perftests/permission/PermissionManagerPerfTest.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.perftests.permission
+
+import android.Manifest
+import android.companion.virtual.VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT
+import android.content.Context
+import android.perftests.utils.PerfStatusReporter
+import android.permission.PermissionManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.compatibility.common.util.AdoptShellPermissionsRule
+import com.android.compatibility.common.util.SystemUtil.runShellCommand
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class PermissionManagerPerfTest {
+    @get:Rule var perfStatusReporter = PerfStatusReporter()
+    @get:Rule
+    val mAdoptShellPermissionsRule =
+        AdoptShellPermissionsRule(
+            InstrumentationRegistry.getInstrumentation().getUiAutomation(),
+            Manifest.permission.INSTALL_PACKAGES,
+            Manifest.permission.DELETE_PACKAGES,
+            Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
+            Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS,
+            Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
+        )
+
+    val context: Context = InstrumentationRegistry.getInstrumentation().targetContext
+    val permissionManager = context.getSystemService(PermissionManager::class.java)!!
+
+    @Before
+    fun setup() {
+        val apkPath = "$APK_DIR${APK_NAME}.apk"
+        runShellCommand("pm install -tg $apkPath")
+    }
+
+    @After
+    fun cleanup() {
+        runShellCommand("pm uninstall $PKG_NAME")
+    }
+
+    @Test
+    fun testGetAllPermissionStates() {
+        val benchmarkState = perfStatusReporter.benchmarkState
+        while (benchmarkState.keepRunning()) {
+            permissionManager.getAllPermissionStates(PKG_NAME, PERSISTENT_DEVICE_ID_DEFAULT)
+        }
+    }
+
+    companion object {
+        private const val APK_DIR = "/data/local/tmp/perftests/"
+        private const val APK_NAME = "UsePermissionApp0"
+        private const val PKG_NAME = "android.perftests.appenumeration0"
+    }
+}
diff --git a/apct-tests/perftests/permission/src/android/perftests/permission/PermissionServicePerfTest.kt b/apct-tests/perftests/permission/src/android/perftests/permission/PermissionServicePerfTest.kt
index 13e67e3..1139835 100644
--- a/apct-tests/perftests/permission/src/android/perftests/permission/PermissionServicePerfTest.kt
+++ b/apct-tests/perftests/permission/src/android/perftests/permission/PermissionServicePerfTest.kt
@@ -36,6 +36,7 @@
 import java.io.InputStreamReader
 import java.util.concurrent.TimeUnit
 import java.util.function.BiConsumer
+import org.junit.Ignore
 
 @RunWith(AndroidJUnit4::class)
 class PermissionServicePerfTest {
@@ -48,6 +49,7 @@
     val mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation()
 
     @Test
+    @Ignore("Fails to capture duration, results in infinite loop and execution timeout")
     fun testInstallPackages() {
         mUiAutomation.executeShellCommand(COMMAND_TRACE_START)
         eventually { assertThat(Trace.isTagEnabled(TRACE_TAG)).isTrue() }
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 11fa7b75..c2aeada 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -619,6 +619,7 @@
      * List of end times for app-IDs that are temporarily marked as being allowed to access
      * the network and acquire wakelocks. Times are in milliseconds.
      */
+    @GuardedBy("this")
     private final SparseArray<Pair<MutableLong, String>> mTempWhitelistAppIdEndTimes
             = new SparseArray<>();
 
@@ -5010,7 +5011,9 @@
                 if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
                     return -1;
                 }
-                dumpTempWhitelistSchedule(pw, false);
+                synchronized (this) {
+                    dumpTempWhitelistScheduleLocked(pw, false);
+                }
             }
         } else if ("except-idle-whitelist".equals(cmd)) {
             getContext().enforceCallingOrSelfPermission(
@@ -5294,7 +5297,7 @@
                     pw.println();
                 }
             }
-            dumpTempWhitelistSchedule(pw, true);
+            dumpTempWhitelistScheduleLocked(pw, true);
 
             size = mTempWhitelistAppIdArray != null ? mTempWhitelistAppIdArray.length : 0;
             if (size > 0) {
@@ -5422,7 +5425,8 @@
         }
     }
 
-    void dumpTempWhitelistSchedule(PrintWriter pw, boolean printTitle) {
+    @GuardedBy("this")
+    void dumpTempWhitelistScheduleLocked(PrintWriter pw, boolean printTitle) {
         final int size = mTempWhitelistAppIdEndTimes.size();
         if (size > 0) {
             String prefix = "";
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
index adee322..f722e41 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/IdleController.java
@@ -48,6 +48,7 @@
     private static final String TAG = "JobScheduler.IdleController";
     // Policy: we decide that we're "idle" if the device has been unused /
     // screen off or dreaming or wireless charging dock idle for at least this long
+    @GuardedBy("mLock")
     final ArraySet<JobStatus> mTrackedTasks = new ArraySet<>();
     IdlenessTracker mIdleTracker;
     private final FlexibilityController mFlexibilityController;
@@ -118,8 +119,10 @@
             for (int i = mTrackedTasks.size()-1; i >= 0; i--) {
                 mTrackedTasks.valueAt(i).setIdleConstraintSatisfied(nowElapsed, isIdle);
             }
+            if (!mTrackedTasks.isEmpty()) {
+                mStateChangedListener.onControllerStateChanged(mTrackedTasks);
+            }
         }
-        mStateChangedListener.onControllerStateChanged(mTrackedTasks);
     }
 
     /**
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 96315eb..50d97cf 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -14435,6 +14435,7 @@
     method @NonNull public android.telephony.CarrierRestrictionRules build();
     method @NonNull public android.telephony.CarrierRestrictionRules.Builder setAllCarriersAllowed();
     method @NonNull public android.telephony.CarrierRestrictionRules.Builder setAllowedCarriers(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>);
+    method @FlaggedApi("com.android.internal.telephony.flags.set_carrier_restriction_status") @NonNull public android.telephony.CarrierRestrictionRules.Builder setCarrierRestrictionStatus(int);
     method @NonNull public android.telephony.CarrierRestrictionRules.Builder setDefaultCarrierRestriction(int);
     method @NonNull public android.telephony.CarrierRestrictionRules.Builder setExcludedCarriers(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>);
     method @NonNull public android.telephony.CarrierRestrictionRules.Builder setMultiSimPolicy(int);
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index fd9600c..65628d3 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -16,6 +16,7 @@
 
 package android.accessibilityservice;
 
+import static android.accessibilityservice.AccessibilityServiceInfo.CAPABILITY_CAN_CONTROL_MAGNIFICATION;
 import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 
@@ -69,6 +70,8 @@
 import android.view.accessibility.AccessibilityWindowInfo;
 import android.view.inputmethod.EditorInfo;
 
+import androidx.annotation.GuardedBy;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.inputmethod.CancellationGroup;
 import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
@@ -640,6 +643,8 @@
         /** The detected gesture information for different displays */
         boolean onGesture(AccessibilityGestureEvent gestureInfo);
         boolean onKeyEvent(KeyEvent event);
+        /** Magnification SystemUI connection changed callbacks */
+        void onMagnificationSystemUIConnectionChanged(boolean connected);
         /** Magnification changed callbacks for different displays */
         void onMagnificationChanged(int displayId, @NonNull Region region,
                 MagnificationConfig config);
@@ -790,7 +795,6 @@
     public static final String KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP =
             "screenshot_timestamp";
 
-
     /**
      * Annotations for result codes of attaching accessibility overlays.
      *
@@ -837,6 +841,13 @@
 
     private WindowManager mWindowManager;
 
+    @GuardedBy("mLock")
+    private boolean mServiceConnected;
+    @GuardedBy("mLock")
+    private boolean mMagnificationSystemUIConnected;
+    @GuardedBy("mLock")
+    private boolean mServiceConnectedNotified;
+
     /** List of magnification controllers, mapping from displayId -> MagnificationController. */
     private final SparseArray<MagnificationController> mMagnificationControllers =
             new SparseArray<>(0);
@@ -886,11 +897,14 @@
             for (int i = 0; i < mMagnificationControllers.size(); i++) {
                 mMagnificationControllers.valueAt(i).onServiceConnectedLocked();
             }
+            checkIsMagnificationSystemUIConnectedAlready();
             final AccessibilityServiceInfo info = getServiceInfo();
             if (info != null) {
                 updateInputMethod(info);
                 mMotionEventSources = info.getMotionEventSources();
             }
+            mServiceConnected = true;
+            mServiceConnectedNotified = false;
         }
         if (mSoftKeyboardController != null) {
             mSoftKeyboardController.onServiceConnected();
@@ -898,7 +912,57 @@
 
         // The client gets to handle service connection last, after we've set
         // up any state upon which their code may rely.
-        onServiceConnected();
+        if (android.view.accessibility.Flags
+                .waitMagnificationSystemUiConnectionToNotifyServiceConnected()) {
+            notifyOnServiceConnectedIfReady();
+        } else {
+            onServiceConnected();
+        }
+    }
+
+    private void notifyOnServiceConnectedIfReady() {
+        synchronized (mLock) {
+            if (mServiceConnectedNotified) {
+                return;
+            }
+            boolean canControlMagnification;
+            final AccessibilityServiceInfo info = getServiceInfo();
+            if (info != null) {
+                int flagMask = CAPABILITY_CAN_CONTROL_MAGNIFICATION;
+                canControlMagnification = (info.getCapabilities() & flagMask) == flagMask;
+            } else {
+                canControlMagnification = false;
+            }
+            boolean ready = canControlMagnification
+                    ? (mServiceConnected && mMagnificationSystemUIConnected)
+                    : mServiceConnected;
+            if (ready) {
+                getMainExecutor().execute(() -> onServiceConnected());
+                mServiceConnectedNotified = true;
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void checkIsMagnificationSystemUIConnectedAlready() {
+        if (!android.view.accessibility.Flags
+                .waitMagnificationSystemUiConnectionToNotifyServiceConnected()) {
+            return;
+        }
+        if (mMagnificationSystemUIConnected) {
+            return;
+        }
+        final IAccessibilityServiceConnection connection =
+                AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId);
+        if (connection != null) {
+            try {
+                boolean connected = connection.isMagnificationSystemUIConnected();
+                mMagnificationSystemUIConnected = connected;
+            } catch (RemoteException re) {
+                Log.w(LOG_TAG, "Failed to check magnification system ui connection", re);
+                re.rethrowFromSystemServer();
+            }
+        }
     }
 
     private void updateInputMethod(AccessibilityServiceInfo info) {
@@ -1360,6 +1424,22 @@
         }
     }
 
+    private void onMagnificationSystemUIConnectionChanged(boolean connected) {
+        if (!android.view.accessibility.Flags
+                .waitMagnificationSystemUiConnectionToNotifyServiceConnected()) {
+            return;
+        }
+
+        synchronized (mLock) {
+            boolean changed = (mMagnificationSystemUIConnected != connected);
+            mMagnificationSystemUIConnected = connected;
+
+            if (changed) {
+                notifyOnServiceConnectedIfReady();
+            }
+        }
+    }
+
     private void onMagnificationChanged(int displayId, @NonNull Region region,
             MagnificationConfig config) {
         MagnificationController controller;
@@ -2823,6 +2903,11 @@
             }
 
             @Override
+            public void onMagnificationSystemUIConnectionChanged(boolean connected) {
+                AccessibilityService.this.onMagnificationSystemUIConnectionChanged(connected);
+            }
+
+            @Override
             public void onMagnificationChanged(int displayId, @NonNull Region region,
                     MagnificationConfig config) {
                 AccessibilityService.this.onMagnificationChanged(displayId, region, config);
@@ -3032,6 +3117,16 @@
             });
         }
 
+        @Override
+        public void onMagnificationSystemUIConnectionChanged(boolean connected) {
+            mExecutor.execute(() -> {
+                if (mConnectionId != AccessibilityInteractionClient.NO_ID) {
+                    mCallback.onMagnificationSystemUIConnectionChanged(connected);
+                }
+                return;
+            });
+        }
+
         /** Magnification changed callbacks for different displays */
         public void onMagnificationChanged(int displayId, @NonNull Region region,
                 MagnificationConfig config) {
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
index 3bc61e5..f1479ef 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceClient.aidl
@@ -48,6 +48,8 @@
 
     void onKeyEvent(in KeyEvent event, int sequence);
 
+    void onMagnificationSystemUIConnectionChanged(boolean connected);
+
     void onMagnificationChanged(int displayId, in Region region, in MagnificationConfig config);
 
     void onMotionEvent(in MotionEvent event);
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 713d8e5..149e719 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -130,6 +130,9 @@
     void setMagnificationCallbackEnabled(int displayId, boolean enabled);
 
     @RequiresNoPermission
+    boolean isMagnificationSystemUIConnected();
+
+    @RequiresNoPermission
     boolean setSoftKeyboardShowMode(int showMode);
 
     @RequiresNoPermission
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 2887d22..fa8fe3b 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1755,6 +1755,12 @@
         private int mNavigationBarColor;
         @Appearance
         private int mSystemBarsAppearance;
+        /**
+         * Similar to {@link TaskDescription#mSystemBarsAppearance}, but is taken from the topmost
+         * fully opaque (i.e. non transparent) activity in the task.
+         */
+        @Appearance
+        private int mTopOpaqueSystemBarsAppearance;
         private boolean mEnsureStatusBarContrastWhenTransparent;
         private boolean mEnsureNavigationBarContrastWhenTransparent;
         private int mResizeMode;
@@ -1855,7 +1861,7 @@
                 final Icon icon = mIconRes == Resources.ID_NULL ? null :
                         Icon.createWithResource(ActivityThread.currentPackageName(), mIconRes);
                 return new TaskDescription(mLabel, icon, mPrimaryColor, mBackgroundColor,
-                        mStatusBarColor, mNavigationBarColor, 0, false, false,
+                        mStatusBarColor, mNavigationBarColor, 0, 0, false, false,
                         RESIZE_MODE_RESIZEABLE, -1, -1, 0);
             }
         }
@@ -1874,7 +1880,7 @@
         @Deprecated
         public TaskDescription(String label, @DrawableRes int iconRes, int colorPrimary) {
             this(label, Icon.createWithResource(ActivityThread.currentPackageName(), iconRes),
-                    colorPrimary, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
+                    colorPrimary, 0, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
             if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) {
                 throw new RuntimeException("A TaskDescription's primary color should be opaque");
             }
@@ -1892,7 +1898,7 @@
         @Deprecated
         public TaskDescription(String label, @DrawableRes int iconRes) {
             this(label, Icon.createWithResource(ActivityThread.currentPackageName(), iconRes),
-                    0, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
+                    0, 0, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
         }
 
         /**
@@ -1904,7 +1910,7 @@
          */
         @Deprecated
         public TaskDescription(String label) {
-            this(label, null, 0, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
+            this(label, null, 0, 0, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
         }
 
         /**
@@ -1914,7 +1920,7 @@
          */
         @Deprecated
         public TaskDescription() {
-            this(null, null, 0, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
+            this(null, null, 0, 0, 0, 0, 0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
         }
 
         /**
@@ -1930,7 +1936,7 @@
         @Deprecated
         public TaskDescription(String label, Bitmap icon, int colorPrimary) {
             this(label, icon != null ? Icon.createWithBitmap(icon) : null, colorPrimary, 0, 0, 0,
-                    0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
+                    0, 0, false, false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
             if ((colorPrimary != 0) && (Color.alpha(colorPrimary) != 255)) {
                 throw new RuntimeException("A TaskDescription's primary color should be opaque");
             }
@@ -1946,7 +1952,7 @@
          */
         @Deprecated
         public TaskDescription(String label, Bitmap icon) {
-            this(label, icon != null ? Icon.createWithBitmap(icon) : null, 0, 0, 0, 0, 0, false,
+            this(label, icon != null ? Icon.createWithBitmap(icon) : null, 0, 0, 0, 0, 0, 0, false,
                     false, RESIZE_MODE_RESIZEABLE, -1, -1, 0);
         }
 
@@ -1955,6 +1961,7 @@
                 int colorPrimary, int colorBackground,
                 int statusBarColor, int navigationBarColor,
                 @Appearance int systemBarsAppearance,
+                @Appearance int topOpaqueSystemBarsAppearance,
                 boolean ensureStatusBarContrastWhenTransparent,
                 boolean ensureNavigationBarContrastWhenTransparent, int resizeMode, int minWidth,
                 int minHeight, int colorBackgroundFloating) {
@@ -1965,6 +1972,7 @@
             mStatusBarColor = statusBarColor;
             mNavigationBarColor = navigationBarColor;
             mSystemBarsAppearance = systemBarsAppearance;
+            mTopOpaqueSystemBarsAppearance = topOpaqueSystemBarsAppearance;
             mEnsureStatusBarContrastWhenTransparent = ensureStatusBarContrastWhenTransparent;
             mEnsureNavigationBarContrastWhenTransparent =
                     ensureNavigationBarContrastWhenTransparent;
@@ -1994,6 +2002,7 @@
             mStatusBarColor = other.mStatusBarColor;
             mNavigationBarColor = other.mNavigationBarColor;
             mSystemBarsAppearance = other.mSystemBarsAppearance;
+            mTopOpaqueSystemBarsAppearance = other.mTopOpaqueSystemBarsAppearance;
             mEnsureStatusBarContrastWhenTransparent = other.mEnsureStatusBarContrastWhenTransparent;
             mEnsureNavigationBarContrastWhenTransparent =
                     other.mEnsureNavigationBarContrastWhenTransparent;
@@ -2026,6 +2035,9 @@
             if (other.mSystemBarsAppearance != 0) {
                 mSystemBarsAppearance = other.mSystemBarsAppearance;
             }
+            if (other.mTopOpaqueSystemBarsAppearance != 0) {
+                mTopOpaqueSystemBarsAppearance = other.mTopOpaqueSystemBarsAppearance;
+            }
 
             mEnsureStatusBarContrastWhenTransparent = other.mEnsureStatusBarContrastWhenTransparent;
             mEnsureNavigationBarContrastWhenTransparent =
@@ -2305,6 +2317,14 @@
         /**
          * @hide
          */
+        @Appearance
+        public int getTopOpaqueSystemBarsAppearance() {
+            return mTopOpaqueSystemBarsAppearance;
+        }
+
+        /**
+         * @hide
+         */
         public void setEnsureStatusBarContrastWhenTransparent(
                 boolean ensureStatusBarContrastWhenTransparent) {
             mEnsureStatusBarContrastWhenTransparent = ensureStatusBarContrastWhenTransparent;
@@ -2320,6 +2340,13 @@
         /**
          * @hide
          */
+        public void setTopOpaqueSystemBarsAppearance(int topOpaqueSystemBarsAppearance) {
+            mTopOpaqueSystemBarsAppearance = topOpaqueSystemBarsAppearance;
+        }
+
+        /**
+         * @hide
+         */
         public boolean getEnsureNavigationBarContrastWhenTransparent() {
             return mEnsureNavigationBarContrastWhenTransparent;
         }
@@ -2442,6 +2469,7 @@
             dest.writeInt(mStatusBarColor);
             dest.writeInt(mNavigationBarColor);
             dest.writeInt(mSystemBarsAppearance);
+            dest.writeInt(mTopOpaqueSystemBarsAppearance);
             dest.writeBoolean(mEnsureStatusBarContrastWhenTransparent);
             dest.writeBoolean(mEnsureNavigationBarContrastWhenTransparent);
             dest.writeInt(mResizeMode);
@@ -2466,6 +2494,7 @@
             mStatusBarColor = source.readInt();
             mNavigationBarColor = source.readInt();
             mSystemBarsAppearance = source.readInt();
+            mTopOpaqueSystemBarsAppearance = source.readInt();
             mEnsureStatusBarContrastWhenTransparent = source.readBoolean();
             mEnsureNavigationBarContrastWhenTransparent = source.readBoolean();
             mResizeMode = source.readInt();
@@ -2498,7 +2527,8 @@
                     + " resizeMode: " + ActivityInfo.resizeModeToString(mResizeMode)
                     + " minWidth: " + mMinWidth + " minHeight: " + mMinHeight
                     + " colorBackgrounFloating: " + mColorBackgroundFloating
-                    + " systemBarsAppearance: " + mSystemBarsAppearance;
+                    + " systemBarsAppearance: " + mSystemBarsAppearance
+                    + " topOpaqueSystemBarsAppearance: " + mTopOpaqueSystemBarsAppearance;
         }
 
         @Override
@@ -2519,6 +2549,7 @@
             result = result * 31 + mStatusBarColor;
             result = result * 31 + mNavigationBarColor;
             result = result * 31 + mSystemBarsAppearance;
+            result = result * 31 + mTopOpaqueSystemBarsAppearance;
             result = result * 31 + (mEnsureStatusBarContrastWhenTransparent ? 1 : 0);
             result = result * 31 + (mEnsureNavigationBarContrastWhenTransparent ? 1 : 0);
             result = result * 31 + mResizeMode;
@@ -2542,6 +2573,7 @@
                     && mStatusBarColor == other.mStatusBarColor
                     && mNavigationBarColor == other.mNavigationBarColor
                     && mSystemBarsAppearance == other.mSystemBarsAppearance
+                    && mTopOpaqueSystemBarsAppearance == other.mTopOpaqueSystemBarsAppearance
                     && mEnsureStatusBarContrastWhenTransparent
                             == other.mEnsureStatusBarContrastWhenTransparent
                     && mEnsureNavigationBarContrastWhenTransparent
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 7dbf270..76c1ed6 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -355,7 +355,7 @@
 
     private static final String DEFAULT_FULL_BACKUP_AGENT = "android.app.backup.FullBackupAgent";
 
-    private static final long BINDER_CALLBACK_THROTTLE_MS = 10_100L;
+    private static final long BINDER_CALLBACK_THROTTLE = 10_100L;
     private long mBinderCallbackLast = -1;
 
     /**
@@ -7551,13 +7551,12 @@
             @Override
             public void onTransactionError(int pid, int code, int flags, int err) {
                 final long now = SystemClock.uptimeMillis();
-                if (now < mBinderCallbackLast + BINDER_CALLBACK_THROTTLE_MS) {
+                if (now < mBinderCallbackLast + BINDER_CALLBACK_THROTTLE) {
                     Slog.d(TAG, "Too many transaction errors, throttling freezer binder callback.");
                     return;
                 }
                 mBinderCallbackLast = now;
                 try {
-                    Log.wtfStack(TAG, "Binder Transaction Error");
                     mgr.frozenBinderTransactionDetected(pid, code, flags, err);
                 } catch (RemoteException ex) {
                     throw ex.rethrowFromSystemServer();
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index c0f7232..5956e2b 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2617,6 +2617,9 @@
         try {
             Objects.requireNonNull(packageName);
             return mPM.isAppArchivable(packageName, new UserHandle(getUserId()));
+        } catch (ParcelableException e) {
+            e.maybeRethrow(NameNotFoundException.class);
+            throw new RuntimeException(e);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 4c839f1..fc3bb02 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -114,7 +114,7 @@
 import com.android.internal.graphics.ColorUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.ContrastColorUtil;
-import com.android.internal.util.NewlineNormalizer;
+import com.android.internal.util.NotificationBigTextNormalizer;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -1632,6 +1632,10 @@
     private Icon mSmallIcon;
     @UnsupportedAppUsage
     private Icon mLargeIcon;
+    private Icon mAppIcon;
+
+    /** Cache for whether the notification was posted by a headless system app. */
+    private Boolean mBelongsToHeadlessSystemApp = null;
 
     @UnsupportedAppUsage
     private String mChannelId;
@@ -3079,25 +3083,17 @@
                     return name.toString();
                 }
             }
-            // If not, try getting the app info from extras.
+            // If not, try getting the name from the app info.
             if (context == null) {
                 return null;
             }
-            final PackageManager pm = context.getPackageManager();
             if (TextUtils.isEmpty(name)) {
-                if (extras.containsKey(EXTRA_BUILDER_APPLICATION_INFO)) {
-                    final ApplicationInfo info = extras.getParcelable(
-                            EXTRA_BUILDER_APPLICATION_INFO,
-                            ApplicationInfo.class);
-                    if (info != null) {
-                        name = pm.getApplicationLabel(info);
-                    }
+                ApplicationInfo info = getApplicationInfo(context);
+                if (info != null) {
+                    final PackageManager pm = context.getPackageManager();
+                    name = pm.getApplicationLabel(getApplicationInfo(context));
                 }
             }
-            // If that's still empty, use the one from the context directly.
-            if (TextUtils.isEmpty(name)) {
-                name = pm.getApplicationLabel(context.getApplicationInfo());
-            }
             // If there's still nothing, ¯\_(ツ)_/¯
             if (TextUtils.isEmpty(name)) {
                 return null;
@@ -3109,9 +3105,89 @@
     }
 
     /**
+     * Whether this notification was posted by a headless system app.
+     *
+     * If we don't have enough information to figure this out, this will return false. Therefore,
+     * false negatives are possible, but false positives should not be.
+     *
      * @hide
      */
-    public int loadHeaderAppIconRes(Context context) {
+    public boolean belongsToHeadlessSystemApp(Context context) {
+        Trace.beginSection("Notification#belongsToHeadlessSystemApp");
+
+        try {
+            if (mBelongsToHeadlessSystemApp != null) {
+                return mBelongsToHeadlessSystemApp;
+            }
+
+            if (context == null) {
+                // Without a valid context, we don't know exactly. Let's assume it doesn't belong to
+                // a system app, but not cache the value.
+                return false;
+            }
+
+            ApplicationInfo info = getApplicationInfo(context);
+            if (info != null) {
+                if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+                    // It's not a system app at all.
+                    mBelongsToHeadlessSystemApp = false;
+                } else {
+                    // If there's no launch intent, it's probably a headless app.
+                    final PackageManager pm = context.getPackageManager();
+                    mBelongsToHeadlessSystemApp = pm.getLaunchIntentForPackage(info.packageName)
+                            == null;
+                }
+            } else {
+                // If for some reason we don't have the app info, we don't know; best assume it's
+                // not a system app.
+                return false;
+            }
+            return mBelongsToHeadlessSystemApp;
+        } finally {
+            Trace.endSection();
+        }
+    }
+
+    /**
+     * Get the resource ID of the app icon from application info.
+     * @hide
+     */
+    public int getHeaderAppIconRes(Context context) {
+        ApplicationInfo info = getApplicationInfo(context);
+        if (info != null) {
+            return info.icon;
+        }
+        return 0;
+    }
+
+    /**
+     * Load the app icon drawable from the package manager. This could result in a binder call.
+     * @hide
+     */
+    public Drawable loadHeaderAppIcon(Context context) {
+        Trace.beginSection("Notification#loadHeaderAppIcon");
+
+        try {
+            if (context == null) {
+                Log.e(TAG, "Cannot load the app icon drawable with a null context");
+                return null;
+            }
+            final PackageManager pm = context.getPackageManager();
+            ApplicationInfo info = getApplicationInfo(context);
+            if (info == null) {
+                Log.e(TAG, "Cannot load the app icon drawable: no application info");
+                return null;
+            }
+            return pm.getApplicationIcon(info);
+        } finally {
+            Trace.endSection();
+        }
+    }
+
+    /**
+     * Fetch the application info from the notification, or the context if that isn't available.
+     */
+    private ApplicationInfo getApplicationInfo(Context context) {
         ApplicationInfo info = null;
         if (extras.containsKey(EXTRA_BUILDER_APPLICATION_INFO)) {
             info = extras.getParcelable(
@@ -3119,12 +3195,12 @@
                     ApplicationInfo.class);
         }
         if (info == null) {
+            if (context == null) {
+                return null;
+            }
             info = context.getApplicationInfo();
         }
-        if (info != null) {
-            return info.icon;
-        }
-        return 0;
+        return info;
     }
 
     /**
@@ -3186,12 +3262,12 @@
         return cs.toString();
     }
 
-    private static CharSequence cleanUpNewLines(@Nullable CharSequence charSequence) {
+    private static CharSequence normalizeBigText(@Nullable CharSequence charSequence) {
         if (charSequence == null) {
             return charSequence;
         }
 
-        return NewlineNormalizer.normalizeNewlines(charSequence.toString());
+        return NotificationBigTextNormalizer.normalizeBigText(charSequence.toString());
     }
 
     private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
@@ -4124,6 +4200,55 @@
     }
 
     /**
+     * The colored app icon that can replace the small icon in the notification starting in V.
+     *
+     * Before using this value, you should first check whether it's actually being used by the
+     * notification by calling {@link Notification#shouldUseAppIcon()}.
+     *
+     * @hide
+     */
+    public Icon getAppIcon() {
+        if (mAppIcon != null) {
+            return mAppIcon;
+        }
+        // If the app icon hasn't been loaded yet, check if we can load it without a context.
+        if (extras.containsKey(EXTRA_BUILDER_APPLICATION_INFO)) {
+            final ApplicationInfo info = extras.getParcelable(
+                    EXTRA_BUILDER_APPLICATION_INFO,
+                    ApplicationInfo.class);
+            if (info != null) {
+                int appIconRes = info.icon;
+                if (appIconRes == 0) {
+                    Log.w(TAG, "Failed to get the app icon: no icon in application info");
+                    return null;
+                }
+                mAppIcon = Icon.createWithResource(info.packageName, appIconRes);
+                return mAppIcon;
+            } else {
+                Log.e(TAG, "Failed to get the app icon: "
+                        + "there's an EXTRA_BUILDER_APPLICATION_INFO in extras but it's null");
+            }
+        } else {
+            Log.w(TAG, "Failed to get the app icon: no application info in extras");
+        }
+        return null;
+    }
+
+    /**
+     * Whether the notification is using the app icon instead of the small icon.
+     * @hide
+     */
+    public boolean shouldUseAppIcon() {
+        if (Flags.notificationsUseAppIconInRow()) {
+            if (belongsToHeadlessSystemApp(/* context = */ null)) {
+                return false;
+            }
+            return getAppIcon() != null;
+        }
+        return false;
+    }
+
+    /**
      * The large icon shown in this notification's content view.
      * @see Builder#getLargeIcon()
      * @see Builder#setLargeIcon(Icon)
@@ -6116,16 +6241,30 @@
             if (Flags.notificationsUseAppIcon()) {
                 // Override small icon with app icon
                 mN.mSmallIcon = Icon.createWithResource(mContext,
-                        mN.loadHeaderAppIconRes(mContext));
+                        mN.getHeaderAppIconRes(mContext));
             } else if (mN.mSmallIcon == null && mN.icon != 0) {
                 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
             }
 
-            contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
+            boolean usingAppIcon = false;
+            if (Flags.notificationsUseAppIconInRow() && !mN.belongsToHeadlessSystemApp(mContext)) {
+                // Use the app icon in the view
+                int appIconRes = mN.getHeaderAppIconRes(mContext);
+                if (appIconRes != 0) {
+                    mN.mAppIcon = Icon.createWithResource(mContext, appIconRes);
+                    contentView.setImageViewIcon(R.id.icon, mN.mAppIcon);
+                    usingAppIcon = true;
+                } else {
+                    Log.w(TAG, "bindSmallIcon: could not get the app icon");
+                }
+            }
+            if (!usingAppIcon) {
+                contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
+            }
             contentView.setInt(R.id.icon, "setImageLevel", mN.iconLevel);
 
             // Don't change color if we're using the app icon.
-            if (!Flags.notificationsUseAppIcon()) {
+            if (!Flags.notificationsUseAppIcon() && !usingAppIcon) {
                 processSmallIconColor(mN.mSmallIcon, contentView, p);
             }
         }
@@ -8427,7 +8566,7 @@
             // Replace the text with the big text, but only if the big text is not empty.
             CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
             if (Flags.cleanUpSpansAndNewLines()) {
-                bigTextText = cleanUpNewLines(stripStyling(bigTextText));
+                bigTextText = normalizeBigText(stripStyling(bigTextText));
             }
             if (!TextUtils.isEmpty(bigTextText)) {
                 p.text(bigTextText);
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 348d4d8f..273a79e 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -1969,6 +1969,11 @@
                 }
 
                 @Override
+                public void onMagnificationSystemUIConnectionChanged(boolean connected) {
+                    /* do nothing */
+                }
+
+                @Override
                 public void onMagnificationChanged(int displayId, @NonNull Region region,
                         MagnificationConfig config) {
                     /* do nothing */
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 44444b5..67752f2 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -62,6 +62,7 @@
 import static android.app.admin.flags.Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED;
 import static android.app.admin.flags.Flags.FLAG_SECURITY_LOG_V2_ENABLED;
 import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
+import static android.app.admin.flags.Flags.onboardingConsentlessBugreports;
 import static android.app.admin.flags.Flags.FLAG_IS_MTE_POLICY_ENFORCED;
 import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
 import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
@@ -17626,6 +17627,17 @@
         return onboardingBugreportV2Enabled();
     }
 
+    // TODO(b/308755220): Remove once the build is finalised.
+    /**
+     * Returns true if the flag for consentless bugreports is enabled.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public boolean isOnboardingConsentlessBugreportFlagEnabled() {
+        return onboardingConsentlessBugreports();
+    }
+
     /**
      * Returns the subscription ids of all subscriptions which were downloaded by the calling
      * admin.
diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig
index 55c3bb60..2d78317 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -52,14 +52,32 @@
   bug: "281044385"
 }
 
+# vvv Prototypes for using app icons in notifications vvv
+
 flag {
   name: "notifications_use_app_icon"
   namespace: "systemui"
-  description: "Experiment to replace the small icon in a notification with the app icon."
+  description: "Experiment to replace the small icon in a notification with the app icon. This includes the status bar, AOD, shelf and notification row itself."
   bug: "335211019"
 }
 
 flag {
+  name: "notifications_use_app_icon_in_row"
+  namespace: "systemui"
+  description: "Experiment to replace the small icon in a notification row with the app icon."
+  bug: "335211019"
+}
+
+flag {
+  name: "notifications_use_monochrome_app_icon"
+  namespace: "systemui"
+  description: "Experiment to replace the notification icon in the status bar and shelf with the monochrome app icon, if available."
+  bug: "335211019"
+}
+
+# ^^^ Prototypes for using app icons in notifications ^^^
+
+flag {
   name: "notification_expansion_optional"
   namespace: "systemui"
   description: "Experiment to restore the pre-S behavior where standard notifications are not expandable unless they have actions."
@@ -174,3 +192,10 @@
   description: "Removes all custom views"
   bug: "342602960"
 }
+
+flag {
+  name: "redact_sensitive_content_notifications_on_lockscreen"
+  namespace: "systemui"
+  description: "redacts notifications on the lockscreen if they have the 'sensitiveContent' flag"
+  bug: "343631648"
+}
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 7dcbeba..7819e1e 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -104,8 +104,8 @@
     public void execute(@NonNull ClientTransactionHandler client,
             @NonNull PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
-        ActivityClientRecord r = new ActivityClientRecord(mActivityToken, mIntent, mIdent, mInfo,
-                mOverrideConfig, mReferrer, mVoiceInteractor, mState, mPersistentState,
+        final ActivityClientRecord r = new ActivityClientRecord(mActivityToken, mIntent, mIdent,
+                mInfo, mOverrideConfig, mReferrer, mVoiceInteractor, mState, mPersistentState,
                 mPendingResults, mPendingNewIntents, mSceneTransitionInfo, mIsForward,
                 mProfilerInfo, client, mAssistToken, mShareableActivityToken, mLaunchedFromBubble,
                 mTaskFragmentToken, mInitialCallerInfoAccessToken, mActivityWindowInfo);
diff --git a/core/java/android/app/servertransaction/WindowStateInsetsControlChangeItem.java b/core/java/android/app/servertransaction/WindowStateInsetsControlChangeItem.java
index ee04f8c..eb31db1 100644
--- a/core/java/android/app/servertransaction/WindowStateInsetsControlChangeItem.java
+++ b/core/java/android/app/servertransaction/WindowStateInsetsControlChangeItem.java
@@ -16,8 +16,6 @@
 
 package android.app.servertransaction;
 
-import static java.util.Objects.requireNonNull;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ClientTransactionHandler;
@@ -29,34 +27,36 @@
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.Objects;
 
 /**
  * Message to deliver window insets control change info.
  * @hide
  */
-public class WindowStateInsetsControlChangeItem extends ClientTransactionItem {
+public class WindowStateInsetsControlChangeItem extends WindowStateTransactionItem {
 
     private static final String TAG = "WindowStateInsetsControlChangeItem";
 
-    private IWindow mWindow;
     private InsetsState mInsetsState;
-    private InsetsSourceControl.Array mActiveControls;
+
+    @VisibleForTesting
+    public InsetsSourceControl.Array mActiveControls;
 
     @Override
-    public void execute(@NonNull ClientTransactionHandler client,
+    public void execute(@NonNull ClientTransactionHandler client, @NonNull IWindow window,
             @NonNull PendingTransactionActions pendingActions) {
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "windowInsetsControlChanged");
-        if (mWindow instanceof InsetsControlChangeListener listener) {
-            listener.onExecutingWindowStateInsetsControlChangeItem();
-        }
         try {
-            mWindow.insetsControlChanged(mInsetsState, mActiveControls);
+            window.insetsControlChanged(mInsetsState, mActiveControls);
         } catch (RemoteException e) {
             // Should be a local call.
             // An exception could happen if the process is restarted. It is safe to ignore since
             // the window should no longer exist.
             Log.w(TAG, "The original window no longer exists in the new process", e);
+            // Prevent leak
+            mActiveControls.release();
         }
         Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
     }
@@ -73,16 +73,21 @@
         if (instance == null) {
             instance = new WindowStateInsetsControlChangeItem();
         }
-        instance.mWindow = requireNonNull(window);
+        instance.setWindow(window);
         instance.mInsetsState = new InsetsState(insetsState, true /* copySources */);
-        instance.mActiveControls = new InsetsSourceControl.Array(activeControls);
+        instance.mActiveControls = new InsetsSourceControl.Array(
+                activeControls, true /* copyControls */);
+        // This source control is an extra copy if the client is not local. By setting
+        // PARCELABLE_WRITE_RETURN_VALUE, the leash will be released at the end of
+        // SurfaceControl.writeToParcel.
+        instance.mActiveControls.setParcelableFlags(PARCELABLE_WRITE_RETURN_VALUE);
 
         return instance;
     }
 
     @Override
     public void recycle() {
-        mWindow = null;
+        super.recycle();
         mInsetsState = null;
         mActiveControls = null;
         ObjectPool.recycle(this);
@@ -93,14 +98,14 @@
     /** Writes to Parcel. */
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeStrongBinder(mWindow.asBinder());
+        super.writeToParcel(dest, flags);
         dest.writeTypedObject(mInsetsState, flags);
         dest.writeTypedObject(mActiveControls, flags);
     }
 
     /** Reads from Parcel. */
     private WindowStateInsetsControlChangeItem(@NonNull Parcel in) {
-        mWindow = IWindow.Stub.asInterface(in.readStrongBinder());
+        super(in);
         mInsetsState = in.readTypedObject(InsetsState.CREATOR);
         mActiveControls = in.readTypedObject(InsetsSourceControl.Array.CREATOR);
 
@@ -122,19 +127,18 @@
         if (this == o) {
             return true;
         }
-        if (o == null || getClass() != o.getClass()) {
+        if (!super.equals(o)) {
             return false;
         }
         final WindowStateInsetsControlChangeItem other = (WindowStateInsetsControlChangeItem) o;
-        return Objects.equals(mWindow, other.mWindow)
-                && Objects.equals(mInsetsState, other.mInsetsState)
+        return Objects.equals(mInsetsState, other.mInsetsState)
                 && Objects.equals(mActiveControls, other.mActiveControls);
     }
 
     @Override
     public int hashCode() {
         int result = 17;
-        result = 31 * result + Objects.hashCode(mWindow);
+        result = 31 * result + super.hashCode();
         result = 31 * result + Objects.hashCode(mInsetsState);
         result = 31 * result + Objects.hashCode(mActiveControls);
         return result;
@@ -142,15 +146,6 @@
 
     @Override
     public String toString() {
-        return "WindowStateInsetsControlChangeItem{window=" + mWindow + "}";
-    }
-
-    /** The interface for IWindow to perform insets control change directly if possible. */
-    public interface InsetsControlChangeListener {
-        /**
-         * Notifies that IWindow#insetsControlChanged is going to be called from
-         * WindowStateInsetsControlChangeItem.
-         */
-        void onExecutingWindowStateInsetsControlChangeItem();
+        return "WindowStateInsetsControlChangeItem{" + super.toString() + "}";
     }
 }
diff --git a/core/java/android/app/servertransaction/WindowStateResizeItem.java b/core/java/android/app/servertransaction/WindowStateResizeItem.java
index da99096..3c1fa4b 100644
--- a/core/java/android/app/servertransaction/WindowStateResizeItem.java
+++ b/core/java/android/app/servertransaction/WindowStateResizeItem.java
@@ -18,8 +18,6 @@
 
 import static android.view.Display.INVALID_DISPLAY;
 
-import static java.util.Objects.requireNonNull;
-
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ClientTransactionHandler;
@@ -39,11 +37,10 @@
  * Message to deliver window resize info.
  * @hide
  */
-public class WindowStateResizeItem extends ClientTransactionItem {
+public class WindowStateResizeItem extends WindowStateTransactionItem {
 
     private static final String TAG = "WindowStateResizeItem";
 
-    private IWindow mWindow;
     private ClientWindowFrames mFrames;
     private boolean mReportDraw;
     private MergedConfiguration mConfiguration;
@@ -59,15 +56,12 @@
     private ActivityWindowInfo mActivityWindowInfo;
 
     @Override
-    public void execute(@NonNull ClientTransactionHandler client,
+    public void execute(@NonNull ClientTransactionHandler client, @NonNull IWindow window,
             @NonNull PendingTransactionActions pendingActions) {
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
                 mReportDraw ? "windowResizedReport" : "windowResized");
-        if (mWindow instanceof ResizeListener listener) {
-            listener.onExecutingWindowStateResizeItem();
-        }
         try {
-            mWindow.resized(mFrames, mReportDraw, mConfiguration, mInsetsState, mForceLayout,
+            window.resized(mFrames, mReportDraw, mConfiguration, mInsetsState, mForceLayout,
                     mAlwaysConsumeSystemBars, mDisplayId, mSyncSeqId, mDragResizing,
                     mActivityWindowInfo);
         } catch (RemoteException e) {
@@ -94,7 +88,7 @@
         if (instance == null) {
             instance = new WindowStateResizeItem();
         }
-        instance.mWindow = requireNonNull(window);
+        instance.setWindow(window);
         instance.mFrames = new ClientWindowFrames(frames);
         instance.mReportDraw = reportDraw;
         instance.mConfiguration = new MergedConfiguration(configuration);
@@ -113,7 +107,7 @@
 
     @Override
     public void recycle() {
-        mWindow = null;
+        super.recycle();
         mFrames = null;
         mReportDraw = false;
         mConfiguration = null;
@@ -132,7 +126,7 @@
     /** Writes to Parcel. */
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeStrongBinder(mWindow.asBinder());
+        super.writeToParcel(dest, flags);
         dest.writeTypedObject(mFrames, flags);
         dest.writeBoolean(mReportDraw);
         dest.writeTypedObject(mConfiguration, flags);
@@ -147,7 +141,7 @@
 
     /** Reads from Parcel. */
     private WindowStateResizeItem(@NonNull Parcel in) {
-        mWindow = IWindow.Stub.asInterface(in.readStrongBinder());
+        super(in);
         mFrames = in.readTypedObject(ClientWindowFrames.CREATOR);
         mReportDraw = in.readBoolean();
         mConfiguration = in.readTypedObject(MergedConfiguration.CREATOR);
@@ -175,12 +169,11 @@
         if (this == o) {
             return true;
         }
-        if (o == null || getClass() != o.getClass()) {
+        if (!super.equals(o)) {
             return false;
         }
         final WindowStateResizeItem other = (WindowStateResizeItem) o;
-        return Objects.equals(mWindow, other.mWindow)
-                && Objects.equals(mFrames, other.mFrames)
+        return Objects.equals(mFrames, other.mFrames)
                 && mReportDraw == other.mReportDraw
                 && Objects.equals(mConfiguration, other.mConfiguration)
                 && Objects.equals(mInsetsState, other.mInsetsState)
@@ -195,7 +188,7 @@
     @Override
     public int hashCode() {
         int result = 17;
-        result = 31 * result + Objects.hashCode(mWindow);
+        result = 31 * result + super.hashCode();
         result = 31 * result + Objects.hashCode(mFrames);
         result = 31 * result + (mReportDraw ? 1 : 0);
         result = 31 * result + Objects.hashCode(mConfiguration);
@@ -211,16 +204,10 @@
 
     @Override
     public String toString() {
-        return "WindowStateResizeItem{window=" + mWindow
+        return "WindowStateResizeItem{" + super.toString()
                 + ", reportDrawn=" + mReportDraw
                 + ", configuration=" + mConfiguration
                 + ", activityWindowInfo=" + mActivityWindowInfo
                 + "}";
     }
-
-    /** The interface for IWindow to perform resize directly if possible. */
-    public interface ResizeListener {
-        /** Notifies that IWindow#resized is going to be called from WindowStateResizeItem. */
-        void onExecutingWindowStateResizeItem();
-    }
 }
diff --git a/core/java/android/app/servertransaction/WindowStateTransactionItem.java b/core/java/android/app/servertransaction/WindowStateTransactionItem.java
new file mode 100644
index 0000000..d556363
--- /dev/null
+++ b/core/java/android/app/servertransaction/WindowStateTransactionItem.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.servertransaction;
+
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ClientTransactionHandler;
+import android.os.Parcel;
+import android.view.IWindow;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Objects;
+
+/**
+ * {@link ClientTransactionItem} to report changes to a window.
+ *
+ * @hide
+ */
+public abstract class WindowStateTransactionItem extends ClientTransactionItem {
+
+    /** The interface for IWindow to perform callback directly if possible. */
+    public interface TransactionListener {
+        /** Notifies that the transaction item is going to be executed. */
+        void onExecutingWindowStateTransactionItem();
+    }
+
+    /** Target window. */
+    private IWindow mWindow;
+
+    WindowStateTransactionItem() {}
+
+    @Override
+    public final void execute(@NonNull ClientTransactionHandler client,
+            @NonNull PendingTransactionActions pendingActions) {
+        if (mWindow instanceof TransactionListener listener) {
+            listener.onExecutingWindowStateTransactionItem();
+        }
+        execute(client, mWindow, pendingActions);
+    }
+
+    /**
+     * Like {@link #execute(ClientTransactionHandler, PendingTransactionActions)},
+     * but take non-null {@link IWindow} as a parameter.
+     */
+    @VisibleForTesting(visibility = PACKAGE)
+    public abstract void execute(@NonNull ClientTransactionHandler client,
+            @NonNull IWindow window, @NonNull PendingTransactionActions pendingActions);
+
+    void setWindow(@NonNull IWindow window) {
+        mWindow = requireNonNull(window);
+    }
+
+    // To be overridden
+
+    WindowStateTransactionItem(@NonNull Parcel in) {
+        mWindow = IWindow.Stub.asInterface(in.readStrongBinder());
+    }
+
+    @CallSuper
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeStrongBinder(mWindow.asBinder());
+    }
+
+    @CallSuper
+    @Override
+    public void recycle() {
+        mWindow = null;
+    }
+
+    // Subclass must override and call super.equals to compare the mActivityToken.
+    @SuppressWarnings("EqualsGetClass")
+    @CallSuper
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        final WindowStateTransactionItem other = (WindowStateTransactionItem) o;
+        return Objects.equals(mWindow, other.mWindow);
+    }
+
+    @CallSuper
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(mWindow);
+    }
+
+    @CallSuper
+    @Override
+    public String toString() {
+        return "mWindow=" + mWindow;
+    }
+}
diff --git a/core/java/android/appwidget/flags.aconfig b/core/java/android/appwidget/flags.aconfig
index 374be6f..18cfca6 100644
--- a/core/java/android/appwidget/flags.aconfig
+++ b/core/java/android/appwidget/flags.aconfig
@@ -40,3 +40,13 @@
   description: "Throttle the widget view updates to mitigate transaction exceptions"
   bug: "326145514"
 }
+
+flag {
+  name: "support_resume_restore_after_reboot"
+  namespace: "app_widgets"
+  description: "Enable support for resume restore widget after reboot by persisting intermediate states to disk"
+  bug: "336976070"
+  metadata {
+      purpose: PURPOSE_BUGFIX
+  }
+}
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 006226e..1e781532 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -52,4 +52,25 @@
     description: "Makes MediaDrm APIs device-aware"
     bug: "303535376"
     is_fixed_read_only: true
-}
\ No newline at end of file
+}
+
+flag {
+    namespace: "virtual_devices"
+    name: "virtual_display_multi_window_mode_support"
+    description: "Add support for WINDOWING_MODE_MULTI_WINDOW to virtual displays by default"
+    is_fixed_read_only: true
+    bug: "341151395"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    namespace: "virtual_devices"
+    name: "intent_interception_action_matching_fix"
+    description: "Do not match intents without actions if the filter has actions"
+    bug: "343805037"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c8cae82..02d62a2 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -8157,6 +8157,9 @@
                 int eq = uri.indexOf('=', i);
                 if (eq < 0) eq = i-1;
                 int semi = uri.indexOf(';', i);
+                if (semi < 0) {
+                    throw new URISyntaxException(uri, "uri end not found");
+                }
                 String value = eq < semi ? Uri.decode(uri.substring(eq + 1, semi)) : "";
 
                 // action
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 270fc32..821034a 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -2431,6 +2431,7 @@
                     statusReceiver, new UserHandle(mUserId));
         } catch (ParcelableException e) {
             e.maybeRethrow(PackageManager.NameNotFoundException.class);
+            throw new RuntimeException(e);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2467,6 +2468,7 @@
         } catch (ParcelableException e) {
             e.maybeRethrow(IOException.class);
             e.maybeRethrow(PackageManager.NameNotFoundException.class);
+            throw new RuntimeException(e);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2499,6 +2501,7 @@
                     userActionIntent, new UserHandle(mUserId));
         } catch (ParcelableException e) {
             e.maybeRethrow(PackageManager.NameNotFoundException.class);
+            throw new RuntimeException(e);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -3465,8 +3468,12 @@
          *              Android S  ({@link android.os.Build.VERSION_CODES#S API 31})</li>
          *              <li>{@link android.os.Build.VERSION_CODES#R API 30} or higher on
          *              Android T ({@link android.os.Build.VERSION_CODES#TIRAMISU API 33})</li>
-         *              <li>{@link android.os.Build.VERSION_CODES#S API 31} or higher <b>after</b>
-         *              Android T ({@link android.os.Build.VERSION_CODES#TIRAMISU API 33})</li>
+         *              <li>{@link android.os.Build.VERSION_CODES#S API 31} or higher on
+         *              Android U ({@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE API 34})
+         *              </li>
+         *              <li>{@link android.os.Build.VERSION_CODES#TIRAMISU API 33} or higher on
+         *              Android V ({@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM API 35})
+         *              </li>
          *          </ul>
          *     </li>
          *     <li>The installer is:
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 2d9881a..282ede3 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4397,8 +4397,6 @@
      * {@link #hasSystemFeature}: The device supports freeform window management.
      * Windows have title bars and can be moved and resized.
      */
-    // If this feature is present, you also need to set
-    // com.android.internal.R.config_freeformWindowManagement to true in your configuration overlay.
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_FREEFORM_WINDOW_MANAGEMENT
             = "android.software.freeform_window_management";
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index e2a131c..5668c54 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -140,6 +140,16 @@
   }
 }
 
+flag {
+    name: "fix_avatar_concurrent_file_write"
+    namespace: "multiuser"
+    description: "Fix potential unexpected behavior due to concurrent file writing"
+    bug: "339351031"
+    metadata {
+    	purpose: PURPOSE_BUGFIX
+  }
+}
+
 # This flag guards the private space feature and all its implementations excluding the APIs. APIs are guarded by android.os.Flags.allow_private_profile.
 flag {
     name: "enable_private_space_features"
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index a720b64..248ef1d 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -2745,17 +2745,6 @@
                 ar.recycle();
                 Log.i(TAG, "...preloaded " + numberOfEntries + " resources in "
                         + (SystemClock.uptimeMillis() - startTime) + "ms.");
-
-                if (sysRes.getBoolean(
-                        com.android.internal.R.bool.config_freeformWindowManagement)) {
-                    startTime = SystemClock.uptimeMillis();
-                    ar = sysRes.obtainTypedArray(
-                            com.android.internal.R.array.preloaded_freeform_multi_window_drawables);
-                    numberOfEntries = preloadDrawables(sysRes, ar);
-                    ar.recycle();
-                    Log.i(TAG, "...preloaded " + numberOfEntries + " resource in "
-                            + (SystemClock.uptimeMillis() - startTime) + "ms.");
-                }
             }
             sysRes.finishPreloading();
         } catch (RuntimeException e) {
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index e3780ed..f54be00 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -733,7 +733,7 @@
      * commits, or is rolled back, either explicitly or by a call to
      * {@link #yieldIfContendedSafely}.
      */
-    // TODO(340874899) Provide an Executor overload
+    @SuppressLint("ExecutorRegistration")
     public void beginTransactionWithListener(
             @Nullable SQLiteTransactionListener transactionListener) {
         beginTransaction(transactionListener, true);
@@ -763,7 +763,7 @@
      *            transaction begins, commits, or is rolled back, either
      *            explicitly or by a call to {@link #yieldIfContendedSafely}.
      */
-    // TODO(340874899) Provide an Executor overload
+    @SuppressLint("ExecutorRegistration")
     public void beginTransactionWithListenerNonExclusive(
             @Nullable SQLiteTransactionListener transactionListener) {
         beginTransaction(transactionListener, false);
@@ -789,7 +789,6 @@
      *   }
      * </pre>
      */
-    // TODO(340874899) Provide an Executor overload
     @SuppressLint("ExecutorRegistration")
     @FlaggedApi(Flags.FLAG_SQLITE_APIS_35)
     public void beginTransactionWithListenerReadOnly(
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 60ad8e8..2d3d252 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -523,23 +523,25 @@
 
                     Handler mainHandler = new Handler(mContext.getMainLooper());
 
-                    for (Map.Entry<DynamicSensorCallback, Handler> entry :
-                            mDynamicSensorCallbacks.entrySet()) {
-                        final DynamicSensorCallback callback = entry.getKey();
-                        Handler handler =
-                                entry.getValue() == null ? mainHandler : entry.getValue();
+                    synchronized (mDynamicSensorCallbacks) {
+                        for (Map.Entry<DynamicSensorCallback, Handler> entry :
+                                mDynamicSensorCallbacks.entrySet()) {
+                            final DynamicSensorCallback callback = entry.getKey();
+                            Handler handler =
+                                    entry.getValue() == null ? mainHandler : entry.getValue();
 
-                        handler.post(new Runnable() {
-                            @Override
-                            public void run() {
-                                for (Sensor s: addedList) {
-                                    callback.onDynamicSensorConnected(s);
+                            handler.post(new Runnable() {
+                                @Override
+                                public void run() {
+                                    for (Sensor s: addedList) {
+                                        callback.onDynamicSensorConnected(s);
+                                    }
+                                    for (Sensor s: removedList) {
+                                        callback.onDynamicSensorDisconnected(s);
+                                    }
                                 }
-                                for (Sensor s: removedList) {
-                                    callback.onDynamicSensorDisconnected(s);
-                                }
-                            }
-                        });
+                            });
+                        }
                     }
 
                     for (Sensor s: removedList) {
@@ -658,13 +660,15 @@
         if (callback == null) {
             throw new IllegalArgumentException("callback cannot be null");
         }
-        if (mDynamicSensorCallbacks.containsKey(callback)) {
-            // has been already registered, ignore
-            return;
-        }
+        synchronized (mDynamicSensorCallbacks) {
+            if (mDynamicSensorCallbacks.containsKey(callback)) {
+                // has been already registered, ignore
+                return;
+            }
 
-        setupDynamicSensorBroadcastReceiver();
-        mDynamicSensorCallbacks.put(callback, handler);
+            setupDynamicSensorBroadcastReceiver();
+            mDynamicSensorCallbacks.put(callback, handler);
+        }
     }
 
     /** @hide */
@@ -673,7 +677,9 @@
         if (DEBUG_DYNAMIC_SENSOR) {
             Log.i(TAG, "Removing dynamic sensor listener");
         }
-        mDynamicSensorCallbacks.remove(callback);
+        synchronized (mDynamicSensorCallbacks) {
+            mDynamicSensorCallbacks.remove(callback);
+        }
     }
 
     /*
diff --git a/core/java/android/hardware/biometrics/flags.aconfig b/core/java/android/hardware/biometrics/flags.aconfig
index 4284ad0..047d1fa 100644
--- a/core/java/android/hardware/biometrics/flags.aconfig
+++ b/core/java/android/hardware/biometrics/flags.aconfig
@@ -32,3 +32,10 @@
   description: "Feature flag for adding a custom content view API to BiometricPrompt.Builder."
   bug: "302735104"
 }
+
+flag {
+  name: "mandatory_biometrics"
+  namespace: "biometrics_framework"
+  description: "This flag controls whether LSKF fallback is removed from biometric prompt when the phone is outside trusted locations"
+  bug: "322081563"
+}
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 342479b..3cc87ea 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -1492,10 +1492,12 @@
     /**
      * <p>Default flash brightness level for manual flash control in SINGLE mode.</p>
      * <p>If flash unit is available this will be greater than or equal to 1 and less
-     * or equal to <code>android.flash.info.singleStrengthMaxLevel</code>.
+     * or equal to {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel}.
      * Note for devices that do not support the manual flash strength control
      * feature, this level will always be equal to 1.</p>
      * <p>This key is available on all devices.</p>
+     *
+     * @see CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL
      */
     @PublicKey
     @NonNull
@@ -1511,13 +1513,15 @@
      * otherwise the value will be equal to 1.</p>
      * <p>Note that this level is just a number of supported levels(the granularity of control).
      * There is no actual physical power units tied to this level.
-     * There is no relation between android.flash.info.torchStrengthMaxLevel and
-     * android.flash.info.singleStrengthMaxLevel i.e. the ratio of
-     * android.flash.info.torchStrengthMaxLevel:android.flash.info.singleStrengthMaxLevel
+     * There is no relation between {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} and
+     * {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} i.e. the ratio of
+     * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}:{@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel}
      * is not guaranteed to be the ratio of actual brightness.</p>
      * <p>This key is available on all devices.</p>
      *
      * @see CaptureRequest#FLASH_MODE
+     * @see CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL
+     * @see CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL
      */
     @PublicKey
     @NonNull
@@ -1528,10 +1532,12 @@
     /**
      * <p>Default flash brightness level for manual flash control in TORCH mode</p>
      * <p>If flash unit is available this will be greater than or equal to 1 and less
-     * or equal to android.flash.info.torchStrengthMaxLevel.
+     * or equal to {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}.
      * Note for the devices that do not support the manual flash strength control feature,
      * this level will always be equal to 1.</p>
      * <p>This key is available on all devices.</p>
+     *
+     * @see CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL
      */
     @PublicKey
     @NonNull
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index c82e7e8..938636f 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -2684,35 +2684,39 @@
      * <p>Flash strength level to be used when manual flash control is active.</p>
      * <p>Flash strength level to use in capture mode i.e. when the applications control
      * flash with either SINGLE or TORCH mode.</p>
-     * <p>Use android.flash.info.singleStrengthMaxLevel and
-     * android.flash.info.torchStrengthMaxLevel to check whether the device supports
+     * <p>Use {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} and
+     * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} to check whether the device supports
      * flash strength control or not.
      * If the values of android.flash.info.singleStrengthMaxLevel and
-     * android.flash.info.torchStrengthMaxLevel are greater than 1,
+     * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} are greater than 1,
      * then the device supports manual flash strength control.</p>
      * <p>If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> TORCH the value must be &gt;= 1
-     * and &lt;= android.flash.info.torchStrengthMaxLevel.
+     * and &lt;= {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}.
      * If the application doesn't set the key and
-     * android.flash.info.torchStrengthMaxLevel &gt; 1,
+     * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} &gt; 1,
      * then the flash will be fired at the default level set by HAL in
-     * android.flash.info.torchStrengthDefaultLevel.
+     * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_DEFAULT_LEVEL android.flash.torchStrengthDefaultLevel}.
      * If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> SINGLE, then the value must be &gt;= 1
-     * and &lt;= android.flash.info.singleStrengthMaxLevel.
+     * and &lt;= {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel}.
      * If the application does not set this key and
-     * android.flash.info.singleStrengthMaxLevel &gt; 1,
+     * {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} &gt; 1,
      * then the flash will be fired at the default level set by HAL
-     * in android.flash.info.singleStrengthDefaultLevel.
+     * in {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL android.flash.singleStrengthDefaultLevel}.
      * If {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to any of ON_AUTO_FLASH, ON_ALWAYS_FLASH,
      * ON_AUTO_FLASH_REDEYE, ON_EXTERNAL_FLASH values, then the strengthLevel will be ignored.</p>
      * <p><b>Range of valid values:</b><br>
-     * <code>[1-android.flash.info.torchStrengthMaxLevel]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is
+     * <code>[1-{@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is
      * set to TORCH;
-     * <code>[1-android.flash.info.singleStrengthMaxLevel]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is
+     * <code>[1-{@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel}]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is
      * set to SINGLE</p>
      * <p>This key is available on all devices.</p>
      *
      * @see CaptureRequest#CONTROL_AE_MODE
      * @see CaptureRequest#FLASH_MODE
+     * @see CameraCharacteristics#FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL
+     * @see CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL
+     * @see CameraCharacteristics#FLASH_TORCH_STRENGTH_DEFAULT_LEVEL
+     * @see CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL
      */
     @PublicKey
     @NonNull
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 1460515..4406a41 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -2977,35 +2977,39 @@
      * <p>Flash strength level to be used when manual flash control is active.</p>
      * <p>Flash strength level to use in capture mode i.e. when the applications control
      * flash with either SINGLE or TORCH mode.</p>
-     * <p>Use android.flash.info.singleStrengthMaxLevel and
-     * android.flash.info.torchStrengthMaxLevel to check whether the device supports
+     * <p>Use {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} and
+     * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} to check whether the device supports
      * flash strength control or not.
      * If the values of android.flash.info.singleStrengthMaxLevel and
-     * android.flash.info.torchStrengthMaxLevel are greater than 1,
+     * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} are greater than 1,
      * then the device supports manual flash strength control.</p>
      * <p>If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> TORCH the value must be &gt;= 1
-     * and &lt;= android.flash.info.torchStrengthMaxLevel.
+     * and &lt;= {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}.
      * If the application doesn't set the key and
-     * android.flash.info.torchStrengthMaxLevel &gt; 1,
+     * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel} &gt; 1,
      * then the flash will be fired at the default level set by HAL in
-     * android.flash.info.torchStrengthDefaultLevel.
+     * {@link CameraCharacteristics#FLASH_TORCH_STRENGTH_DEFAULT_LEVEL android.flash.torchStrengthDefaultLevel}.
      * If the {@link CaptureRequest#FLASH_MODE android.flash.mode} <code>==</code> SINGLE, then the value must be &gt;= 1
-     * and &lt;= android.flash.info.singleStrengthMaxLevel.
+     * and &lt;= {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel}.
      * If the application does not set this key and
-     * android.flash.info.singleStrengthMaxLevel &gt; 1,
+     * {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel} &gt; 1,
      * then the flash will be fired at the default level set by HAL
-     * in android.flash.info.singleStrengthDefaultLevel.
+     * in {@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL android.flash.singleStrengthDefaultLevel}.
      * If {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is set to any of ON_AUTO_FLASH, ON_ALWAYS_FLASH,
      * ON_AUTO_FLASH_REDEYE, ON_EXTERNAL_FLASH values, then the strengthLevel will be ignored.</p>
      * <p><b>Range of valid values:</b><br>
-     * <code>[1-android.flash.info.torchStrengthMaxLevel]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is
+     * <code>[1-{@link CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL android.flash.torchStrengthMaxLevel}]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is
      * set to TORCH;
-     * <code>[1-android.flash.info.singleStrengthMaxLevel]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is
+     * <code>[1-{@link CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL android.flash.singleStrengthMaxLevel}]</code> when the {@link CaptureRequest#FLASH_MODE android.flash.mode} is
      * set to SINGLE</p>
      * <p>This key is available on all devices.</p>
      *
      * @see CaptureRequest#CONTROL_AE_MODE
      * @see CaptureRequest#FLASH_MODE
+     * @see CameraCharacteristics#FLASH_SINGLE_STRENGTH_DEFAULT_LEVEL
+     * @see CameraCharacteristics#FLASH_SINGLE_STRENGTH_MAX_LEVEL
+     * @see CameraCharacteristics#FLASH_TORCH_STRENGTH_DEFAULT_LEVEL
+     * @see CameraCharacteristics#FLASH_TORCH_STRENGTH_MAX_LEVEL
      */
     @PublicKey
     @NonNull
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 9eabc8d..53771e3 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -963,11 +963,10 @@
      * originate from the system, just that we were unable to verify it. This can
      * happen for a number of reasons during normal operation.
      *
-     * @param event The {@link android.view.InputEvent} to check
+     * @param event The {@link android.view.InputEvent} to check.
      *
      * @return {@link android.view.VerifiedInputEvent}, which is a subset of the provided
-     * {@link android.view.InputEvent}
-     *         {@code null} if the event could not be verified.
+     *     {@link android.view.InputEvent}, or {@code null} if the event could not be verified.
      */
     @Nullable
     public VerifiedInputEvent verifyInputEvent(@NonNull InputEvent event) {
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index 91c2965..c9f207c 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -305,15 +305,28 @@
     /**
      * Interface for receiving a callback when the process hosting an IBinder
      * has gone away.
-     * 
+     *
      * @see #linkToDeath
      */
     public interface DeathRecipient {
         public void binderDied();
 
         /**
-         * Interface for receiving a callback when the process hosting an IBinder
+         * The function called when the process hosting an IBinder
          * has gone away.
+         *
+         * This callback will be called from any binder thread like any other binder
+         * transaction. If the process receiving this notification is multithreaded
+         * then synchronization may be required because other threads may be executing
+         * at the same time.
+         *
+         * No locks are held in libbinder when {@link binderDied} is called.
+         *
+         * There is no need to call {@link unlinkToDeath} in the binderDied callback.
+         * The binder is already dead so {@link unlinkToDeath} is a no-op.
+         * It will be unlinked when the last local reference of that binder proxy is
+         * dropped.
+         *
          * @param who The IBinder that has become invalid
          */
         default void binderDied(@NonNull IBinder who) {
diff --git a/core/java/android/os/OWNERS b/core/java/android/os/OWNERS
index 9f3364f..04d4970 100644
--- a/core/java/android/os/OWNERS
+++ b/core/java/android/os/OWNERS
@@ -107,4 +107,8 @@
 per-file ProfilingServiceManager.java = file:/PERFORMANCE_OWNERS
 
 # Memory
-per-file OomKillRecord.java = file:/MEMORY_OWNERS
\ No newline at end of file
+per-file OomKillRecord.java = file:/MEMORY_OWNERS
+
+# MessageQueue
+per-file MessageQueue.java = mfasheh@google.com, shayba@google.com
+per-file Message.java = mfasheh@google.com, shayba@google.com
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a409537..7933212 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1736,24 +1736,6 @@
             "android.settings.NETWORK_OPERATOR_SETTINGS";
 
     /**
-     * Activity Action: Show settings for selecting the network provider.
-     * <p>
-     * In some cases, a matching Activity may not be provided, so ensure you
-     * safeguard against this.
-     * <p>
-     * Access to this preference can be customized via Settings' app.
-     * <p>
-     * Input: Nothing.
-     * <p>
-     * Output: Nothing.
-     *
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_NETWORK_PROVIDER_SETTINGS =
-            "android.settings.NETWORK_PROVIDER_SETTINGS";
-
-    /**
      * Activity Action: Show settings for selection of 2G/3G.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
@@ -19948,6 +19930,12 @@
             public static final String NETWORK_LOCATION_OPT_IN = "network_location_opt_in";
 
             /**
+             * Whether haptics are enabled for Active Unlock on wear.
+             * @hide
+             */
+            public static final String VIBRATE_FOR_ACTIVE_UNLOCK = "wear_vibrate_for_active_unlock";
+
+            /**
              * The custom foreground color.
              * @hide
              */
diff --git a/core/java/android/security/responsible_apis_flags.aconfig b/core/java/android/security/responsible_apis_flags.aconfig
index c7d951b..56d3669 100644
--- a/core/java/android/security/responsible_apis_flags.aconfig
+++ b/core/java/android/security/responsible_apis_flags.aconfig
@@ -45,3 +45,10 @@
     description: "Do not allow intents without an action to match any intent filters"
     bug: "293560872"
 }
+
+flag {
+    name: "asm_opt_system_into_enforcement"
+    namespace: "responsible_apis"
+    description: "Opt the system into enforcement of BAL"
+    bug: "339403750"
+}
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 71066ac..3f9c819 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -1276,13 +1276,22 @@
         });
     }
 
+    /**
+     * Whether or not wake requests will be redirected.
+     *
+     * @hide
+     */
+    public boolean getRedirectWake() {
+        return mOverlayConnection != null && mRedirectWake;
+    }
+
     private void wakeUp(boolean fromSystem) {
         if (mDebug) {
             Slog.v(mTag, "wakeUp(): fromSystem=" + fromSystem + ", mWaking=" + mWaking
                     + ", mFinished=" + mFinished);
         }
 
-        if (!fromSystem && mOverlayConnection != null && mRedirectWake) {
+        if (!fromSystem && getRedirectWake()) {
             mOverlayConnection.addConsumer(overlay -> {
                 try {
                     overlay.onWakeRequested();
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index c674968..0dec13f 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -18,9 +18,10 @@
 
 import static com.android.graphics.hwui.flags.Flags.highContrastTextLuminance;
 import static com.android.text.flags.Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE;
-import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
 import static com.android.text.flags.Flags.FLAG_LETTER_SPACING_JUSTIFICATION;
+import static com.android.text.flags.Flags.FLAG_USE_BOUNDS_FOR_WIDTH;
 
+import android.annotation.ColorInt;
 import android.annotation.FlaggedApi;
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
@@ -398,6 +399,20 @@
         mUseBoundsForWidth = useBoundsForWidth;
         mShiftDrawingOffsetForStartOverhang = shiftDrawingOffsetForStartOverhang;
         mMinimumFontMetrics = minimumFontMetrics;
+
+        initSpanColors();
+    }
+
+    private void initSpanColors() {
+        if (mSpannedText && Flags.highContrastTextSmallTextRect()) {
+            if (mSpanColors == null) {
+                mSpanColors = new SpanColors();
+            } else {
+                mSpanColors.recycle();
+            }
+        } else {
+            mSpanColors = null;
+        }
     }
 
     /**
@@ -417,6 +432,7 @@
         mSpacingMult = spacingmult;
         mSpacingAdd = spacingadd;
         mSpannedText = text instanceof Spanned;
+        initSpanColors();
     }
 
     /**
@@ -643,20 +659,20 @@
             return null;
         }
 
-        return isHighContrastTextDark() ? BlendMode.MULTIPLY : BlendMode.DIFFERENCE;
+        return isHighContrastTextDark(mPaint.getColor()) ? BlendMode.MULTIPLY
+                : BlendMode.DIFFERENCE;
     }
 
-    private boolean isHighContrastTextDark() {
+    private boolean isHighContrastTextDark(@ColorInt int color) {
         // High-contrast text mode
         // Determine if the text is black-on-white or white-on-black, so we know what blendmode will
         // give the highest contrast and most realistic text color.
         // This equation should match the one in libs/hwui/hwui/DrawTextFunctor.h
         if (highContrastTextLuminance()) {
             var lab = new double[3];
-            ColorUtils.colorToLAB(mPaint.getColor(), lab);
-            return lab[0] < 0.5;
+            ColorUtils.colorToLAB(color, lab);
+            return lab[0] < 50.0;
         } else {
-            var color = mPaint.getColor();
             int channelSum = Color.red(color) + Color.green(color) + Color.blue(color);
             return channelSum < (128 * 3);
         }
@@ -1010,15 +1026,22 @@
         var padding = Math.max(HIGH_CONTRAST_TEXT_BORDER_WIDTH_MIN_PX,
                 mPaint.getTextSize() * HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR);
 
+        var originalTextColor = mPaint.getColor();
         var bgPaint = mWorkPlainPaint;
         bgPaint.reset();
-        bgPaint.setColor(isHighContrastTextDark() ? Color.WHITE : Color.BLACK);
+        bgPaint.setColor(isHighContrastTextDark(originalTextColor) ? Color.WHITE : Color.BLACK);
         bgPaint.setStyle(Paint.Style.FILL);
 
         int start = getLineStart(firstLine);
         int end = getLineEnd(lastLine);
         // Draw a separate background rectangle for each line of text, that only surrounds the
-        // characters on that line.
+        // characters on that line. But we also have to check the text color for each character, and
+        // make sure we are drawing the correct contrasting background. This is because Spans can
+        // change colors throughout the text and we'll need to match our backgrounds.
+        if (mSpannedText && mSpanColors != null) {
+            mSpanColors.init(mWorkPaint, ((Spanned) mText), start, end);
+        }
+
         forEachCharacterBounds(
                 start,
                 end,
@@ -1028,13 +1051,24 @@
                     int mLastLineNum = -1;
                     final RectF mLineBackground = new RectF();
 
+                    @ColorInt int mLastColor = originalTextColor;
+
                     @Override
                     public void onCharacterBounds(int index, int lineNum, float left, float top,
                             float right, float bottom) {
-                        if (lineNum != mLastLineNum) {
+
+                        var newBackground = determineContrastingBackgroundColor(index);
+                        var hasBgColorChanged = newBackground != bgPaint.getColor();
+
+                        if (lineNum != mLastLineNum || hasBgColorChanged) {
+                            // Draw what we have so far, then reset the rect and update its color
                             drawRect();
                             mLineBackground.set(left, top, right, bottom);
                             mLastLineNum = lineNum;
+
+                            if (hasBgColorChanged) {
+                                bgPaint.setColor(newBackground);
+                            }
                         } else {
                             mLineBackground.union(left, top, right, bottom);
                         }
@@ -1051,8 +1085,36 @@
                             canvas.drawRect(mLineBackground, bgPaint);
                         }
                     }
+
+                    private int determineContrastingBackgroundColor(int index) {
+                        if (!mSpannedText || mSpanColors == null) {
+                            // The text is not Spanned. it's all one color.
+                            return bgPaint.getColor();
+                        }
+
+                        // Sometimes the color will change, but not enough to warrant a background
+                        // color change. e.g. from black to dark grey still gets clamped to black,
+                        // so the background stays white and we don't need to draw a fresh
+                        // background.
+                        var textColor = mSpanColors.getColorAt(index);
+                        if (textColor == SpanColors.NO_COLOR_FOUND) {
+                            textColor = originalTextColor;
+                        }
+                        var hasColorChanged = textColor != mLastColor;
+                        if (hasColorChanged) {
+                            mLastColor = textColor;
+
+                            return isHighContrastTextDark(textColor) ? Color.WHITE : Color.BLACK;
+                        }
+
+                        return bgPaint.getColor();
+                    }
                 }
         );
+
+        if (mSpanColors != null) {
+            mSpanColors.recycle();
+        }
     }
 
     /**
@@ -3580,6 +3642,7 @@
     private float mSpacingAdd;
     private static final Rect sTempRect = new Rect();
     private boolean mSpannedText;
+    @Nullable private SpanColors mSpanColors;
     private TextDirectionHeuristic mTextDir;
     private SpanSet<LineBackgroundSpan> mLineBackgroundSpans;
     private boolean mIncludePad;
diff --git a/core/java/android/text/SpanColors.java b/core/java/android/text/SpanColors.java
new file mode 100644
index 0000000..fcd242b
--- /dev/null
+++ b/core/java/android/text/SpanColors.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.text;
+
+import android.annotation.ColorInt;
+import android.annotation.Nullable;
+import android.graphics.Color;
+import android.text.style.CharacterStyle;
+
+/**
+ * Finds the foreground text color for the given Spanned text so you can iterate through each color
+ * change.
+ *
+ * @hide
+ */
+public class SpanColors {
+    public static final @ColorInt int NO_COLOR_FOUND = Color.TRANSPARENT;
+
+    private final SpanSet<CharacterStyle> mCharacterStyleSpanSet =
+            new SpanSet<>(CharacterStyle.class);
+    @Nullable private TextPaint mWorkPaint;
+
+    public SpanColors() {}
+
+    /**
+     * Init for the given text
+     *
+     * @param workPaint A temporary TextPaint object that will be used to calculate the colors. The
+     *                  paint properties will be mutated on calls to {@link #getColorAt(int)} so
+     *                  make sure to reset it before you use it for something else.
+     * @param spanned the text to examine
+     * @param start index to start at
+     * @param end index of the end
+     */
+    public void init(TextPaint workPaint, Spanned spanned, int start, int end) {
+        mWorkPaint = workPaint;
+        mCharacterStyleSpanSet.init(spanned, start, end);
+    }
+
+    /**
+     * Removes all internal references to the spans to avoid memory leaks.
+     */
+    public void recycle() {
+        mWorkPaint = null;
+        mCharacterStyleSpanSet.recycle();
+    }
+
+    /**
+     * Calculates the foreground color of the text at the given character index.
+     *
+     * <p>You must call {@link #init(TextPaint, Spanned, int, int)} before calling this
+     */
+    public @ColorInt int getColorAt(int index) {
+        var finalColor = NO_COLOR_FOUND;
+        // Reset the paint so if we get a CharacterStyle that doesn't actually specify color,
+        // (like UnderlineSpan), we still return no color found.
+        mWorkPaint.setColor(finalColor);
+        for (int k = 0; k < mCharacterStyleSpanSet.numberOfSpans; k++) {
+            if ((index >= mCharacterStyleSpanSet.spanStarts[k])
+                    && (index <= mCharacterStyleSpanSet.spanEnds[k])) {
+                final CharacterStyle span = mCharacterStyleSpanSet.spans[k];
+                span.updateDrawState(mWorkPaint);
+
+                finalColor = calculateFinalColor(mWorkPaint);
+            }
+        }
+        return finalColor;
+    }
+
+    private @ColorInt int calculateFinalColor(TextPaint workPaint) {
+        // TODO: can we figure out what the getColorFilter() will do?
+        //  if so, we also need to reset colorFilter before the loop in getColorAt()
+        return workPaint.getColor();
+    }
+}
diff --git a/core/java/android/tracing/perfetto/InitArguments.java b/core/java/android/tracing/perfetto/InitArguments.java
index da8c273..b4cb68c 100644
--- a/core/java/android/tracing/perfetto/InitArguments.java
+++ b/core/java/android/tracing/perfetto/InitArguments.java
@@ -26,6 +26,7 @@
  */
 public class InitArguments {
     public final @PerfettoBackend int backends;
+    public final int shmemSizeHintKb;
 
     /**
      * @hide
@@ -44,11 +45,21 @@
     // on Linux/Android/Mac uses a named UNIX socket).
     public static final int PERFETTO_BACKEND_SYSTEM = (1 << 1);
 
-    public static InitArguments DEFAULTS = new InitArguments(PERFETTO_BACKEND_SYSTEM);
+    public static InitArguments DEFAULTS = new InitArguments(PERFETTO_BACKEND_SYSTEM, 0);
 
-    public static InitArguments TESTING = new InitArguments(PERFETTO_BACKEND_IN_PROCESS);
+    public static InitArguments TESTING = new InitArguments(PERFETTO_BACKEND_IN_PROCESS, 0);
 
-    public InitArguments(@PerfettoBackend int backends) {
+    /**
+     * Perfetto initialization arguments.
+     *
+     * @param backends Bitwise-or of backends that should be enabled.
+     * @param shmemSizeHintKb [Optional] Tune the size of the shared memory buffer between the
+     *  current process and the service backend(s). This is a trade-off between memory footprint and
+     *  the ability to sustain bursts of trace writes. If set, the value must be a multiple of 4KB.
+     *  The value can be ignored if larger than kMaxShmSize (32MB) or not a multiple of 4KB.
+     */
+    public InitArguments(@PerfettoBackend int backends, int shmemSizeHintKb) {
         this.backends = backends;
+        this.shmemSizeHintKb = shmemSizeHintKb;
     }
 }
diff --git a/core/java/android/tracing/perfetto/Producer.java b/core/java/android/tracing/perfetto/Producer.java
index a1b3eb7..13582e8 100644
--- a/core/java/android/tracing/perfetto/Producer.java
+++ b/core/java/android/tracing/perfetto/Producer.java
@@ -27,8 +27,8 @@
      * @param args arguments on how to initialize the Perfetto producer.
      */
     public static void init(InitArguments args) {
-        nativePerfettoProducerInit(args.backends);
+        nativePerfettoProducerInit(args.backends, args.shmemSizeHintKb);
     }
 
-    private static native void nativePerfettoProducerInit(int backends);
+    private static native void nativePerfettoProducerInit(int backends, int shmemSizeHintKb);
 }
diff --git a/core/java/android/view/ContentRecordingSession.java b/core/java/android/view/ContentRecordingSession.java
index dc41b70..952c63b 100644
--- a/core/java/android/view/ContentRecordingSession.java
+++ b/core/java/android/view/ContentRecordingSession.java
@@ -58,6 +58,15 @@
     /** Can't report (e.g. side loaded app). */
     public static final int TARGET_UID_UNKNOWN = -2;
 
+    /** Task id is not set either because full screen capture or launching a new app */
+    public static final int TASK_ID_UNKNOWN = -1;
+
+    /**
+     * Id of Task that is launched to be captured for a single app capture session. The value may be
+     * {@link #TASK_ID_UNKNOWN} if the session is not for a single app capture.
+     */
+    private int mTaskId = TASK_ID_UNKNOWN;
+
     /**
      * Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has
      * recorded content rendered to its surface.
@@ -115,16 +124,16 @@
     /** Returns an instance initialized for task recording. */
     public static ContentRecordingSession createTaskSession(
             @NonNull IBinder taskWindowContainerToken) {
-        return createTaskSession(taskWindowContainerToken, TARGET_UID_UNKNOWN);
+        return createTaskSession(taskWindowContainerToken, TASK_ID_UNKNOWN);
     }
 
     /** Returns an instance initialized for task recording. */
     public static ContentRecordingSession createTaskSession(
-            @NonNull IBinder taskWindowContainerToken, int targetUid) {
+            @NonNull IBinder taskWindowContainerToken, int taskId) {
         return new ContentRecordingSession()
                 .setContentToRecord(RECORD_CONTENT_TASK)
                 .setTokenToRecord(taskWindowContainerToken)
-                .setTargetUid(targetUid);
+                .setTaskId(taskId);
     }
 
     /**
@@ -211,12 +220,14 @@
 
     @DataClass.Generated.Member
     /* package-private */ ContentRecordingSession(
+            int taskId,
             int virtualDisplayId,
             @RecordContent int contentToRecord,
             int displayToRecord,
             @Nullable IBinder tokenToRecord,
             boolean waitingForConsent,
             int targetUid) {
+        this.mTaskId = taskId;
         this.mVirtualDisplayId = virtualDisplayId;
         this.mContentToRecord = contentToRecord;
 
@@ -237,6 +248,15 @@
     }
 
     /**
+     * Id of Task that is launched to be captured for a single app capture session. The value may be
+     * {@link #TASK_ID_UNKNOWN} if the session is not for a single app capture.
+     */
+    @DataClass.Generated.Member
+    public int getTaskId() {
+        return mTaskId;
+    }
+
+    /**
      * Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has
      * recorded content rendered to its surface.
      */
@@ -295,6 +315,16 @@
     }
 
     /**
+     * Id of Task that is launched to be captured for a single app capture session. The value may be
+     * {@link #TASK_ID_UNKNOWN} if the session is not for a single app capture.
+     */
+    @DataClass.Generated.Member
+    public @NonNull ContentRecordingSession setTaskId( int value) {
+        mTaskId = value;
+        return this;
+    }
+
+    /**
      * Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has
      * recorded content rendered to its surface.
      */
@@ -374,6 +404,7 @@
         // String fieldNameToString() { ... }
 
         return "ContentRecordingSession { " +
+                "taskId = " + mTaskId + ", " +
                 "virtualDisplayId = " + mVirtualDisplayId + ", " +
                 "contentToRecord = " + recordContentToString(mContentToRecord) + ", " +
                 "displayToRecord = " + mDisplayToRecord + ", " +
@@ -396,6 +427,7 @@
         ContentRecordingSession that = (ContentRecordingSession) o;
         //noinspection PointlessBooleanExpression
         return true
+                && mTaskId == that.mTaskId
                 && mVirtualDisplayId == that.mVirtualDisplayId
                 && mContentToRecord == that.mContentToRecord
                 && mDisplayToRecord == that.mDisplayToRecord
@@ -411,6 +443,7 @@
         // int fieldNameHashCode() { ... }
 
         int _hash = 1;
+        _hash = 31 * _hash + mTaskId;
         _hash = 31 * _hash + mVirtualDisplayId;
         _hash = 31 * _hash + mContentToRecord;
         _hash = 31 * _hash + mDisplayToRecord;
@@ -427,9 +460,10 @@
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
         byte flg = 0;
-        if (mWaitingForConsent) flg |= 0x10;
-        if (mTokenToRecord != null) flg |= 0x8;
+        if (mWaitingForConsent) flg |= 0x20;
+        if (mTokenToRecord != null) flg |= 0x10;
         dest.writeByte(flg);
+        dest.writeInt(mTaskId);
         dest.writeInt(mVirtualDisplayId);
         dest.writeInt(mContentToRecord);
         dest.writeInt(mDisplayToRecord);
@@ -449,13 +483,15 @@
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
         byte flg = in.readByte();
-        boolean waitingForConsent = (flg & 0x10) != 0;
+        boolean waitingForConsent = (flg & 0x20) != 0;
+        int taskId = in.readInt();
         int virtualDisplayId = in.readInt();
         int contentToRecord = in.readInt();
         int displayToRecord = in.readInt();
-        IBinder tokenToRecord = (flg & 0x8) == 0 ? null : (IBinder) in.readStrongBinder();
+        IBinder tokenToRecord = (flg & 0x10) == 0 ? null : (IBinder) in.readStrongBinder();
         int targetUid = in.readInt();
 
+        this.mTaskId = taskId;
         this.mVirtualDisplayId = virtualDisplayId;
         this.mContentToRecord = contentToRecord;
 
@@ -496,6 +532,7 @@
     @DataClass.Generated.Member
     public static final class Builder {
 
+        private int mTaskId;
         private int mVirtualDisplayId;
         private @RecordContent int mContentToRecord;
         private int mDisplayToRecord;
@@ -509,13 +546,25 @@
         }
 
         /**
+         * Id of Task that is launched to be captured for a single app capture session. The value may be
+         * {@link #TASK_ID_UNKNOWN} if the session is not for a single app capture.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setTaskId(int value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x1;
+            mTaskId = value;
+            return this;
+        }
+
+        /**
          * Unique logical identifier of the {@link android.hardware.display.VirtualDisplay} that has
          * recorded content rendered to its surface.
          */
         @DataClass.Generated.Member
         public @NonNull Builder setVirtualDisplayId(int value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x1;
+            mBuilderFieldsSet |= 0x2;
             mVirtualDisplayId = value;
             return this;
         }
@@ -526,7 +575,7 @@
         @DataClass.Generated.Member
         public @NonNull Builder setContentToRecord(@RecordContent int value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x2;
+            mBuilderFieldsSet |= 0x4;
             mContentToRecord = value;
             return this;
         }
@@ -540,7 +589,7 @@
         @DataClass.Generated.Member
         public @NonNull Builder setDisplayToRecord(int value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x4;
+            mBuilderFieldsSet |= 0x8;
             mDisplayToRecord = value;
             return this;
         }
@@ -554,7 +603,7 @@
         @DataClass.Generated.Member
         public @NonNull Builder setTokenToRecord(@NonNull IBinder value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x8;
+            mBuilderFieldsSet |= 0x10;
             mTokenToRecord = value;
             return this;
         }
@@ -568,7 +617,7 @@
         @DataClass.Generated.Member
         public @NonNull Builder setWaitingForConsent(boolean value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x10;
+            mBuilderFieldsSet |= 0x20;
             mWaitingForConsent = value;
             return this;
         }
@@ -579,7 +628,7 @@
         @DataClass.Generated.Member
         public @NonNull Builder setTargetUid(int value) {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x20;
+            mBuilderFieldsSet |= 0x40;
             mTargetUid = value;
             return this;
         }
@@ -587,27 +636,31 @@
         /** Builds the instance. This builder should not be touched after calling this! */
         public @NonNull ContentRecordingSession build() {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x40; // Mark builder used
+            mBuilderFieldsSet |= 0x80; // Mark builder used
 
             if ((mBuilderFieldsSet & 0x1) == 0) {
-                mVirtualDisplayId = INVALID_DISPLAY;
+                mTaskId = TASK_ID_UNKNOWN;
             }
             if ((mBuilderFieldsSet & 0x2) == 0) {
-                mContentToRecord = RECORD_CONTENT_DISPLAY;
+                mVirtualDisplayId = INVALID_DISPLAY;
             }
             if ((mBuilderFieldsSet & 0x4) == 0) {
-                mDisplayToRecord = INVALID_DISPLAY;
+                mContentToRecord = RECORD_CONTENT_DISPLAY;
             }
             if ((mBuilderFieldsSet & 0x8) == 0) {
-                mTokenToRecord = null;
+                mDisplayToRecord = INVALID_DISPLAY;
             }
             if ((mBuilderFieldsSet & 0x10) == 0) {
-                mWaitingForConsent = false;
+                mTokenToRecord = null;
             }
             if ((mBuilderFieldsSet & 0x20) == 0) {
+                mWaitingForConsent = false;
+            }
+            if ((mBuilderFieldsSet & 0x40) == 0) {
                 mTargetUid = TARGET_UID_UNKNOWN;
             }
             ContentRecordingSession o = new ContentRecordingSession(
+                    mTaskId,
                     mVirtualDisplayId,
                     mContentToRecord,
                     mDisplayToRecord,
@@ -618,7 +671,7 @@
         }
 
         private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x40) != 0) {
+            if ((mBuilderFieldsSet & 0x80) != 0) {
                 throw new IllegalStateException(
                         "This Builder should not be reused. Use a new Builder instance instead");
             }
@@ -626,10 +679,10 @@
     }
 
     @DataClass.Generated(
-            time = 1697456140720L,
+            time = 1716481148184L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/view/ContentRecordingSession.java",
-            inputSignatures = "public static final  int RECORD_CONTENT_DISPLAY\npublic static final  int RECORD_CONTENT_TASK\npublic static final  int TARGET_UID_FULL_SCREEN\npublic static final  int TARGET_UID_UNKNOWN\nprivate  int mVirtualDisplayId\nprivate @android.view.ContentRecordingSession.RecordContent int mContentToRecord\nprivate  int mDisplayToRecord\nprivate @android.annotation.Nullable android.os.IBinder mTokenToRecord\nprivate  boolean mWaitingForConsent\nprivate  int mTargetUid\npublic static  android.view.ContentRecordingSession createDisplaySession(int)\npublic static  android.view.ContentRecordingSession createTaskSession(android.os.IBinder)\npublic static  android.view.ContentRecordingSession createTaskSession(android.os.IBinder,int)\npublic static  boolean isValid(android.view.ContentRecordingSession)\npublic static  boolean isProjectionOnSameDisplay(android.view.ContentRecordingSession,android.view.ContentRecordingSession)\nclass ContentRecordingSession extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genSetters=true, genEqualsHashCode=true)")
+            inputSignatures = "public static final  int RECORD_CONTENT_DISPLAY\npublic static final  int RECORD_CONTENT_TASK\npublic static final  int TARGET_UID_FULL_SCREEN\npublic static final  int TARGET_UID_UNKNOWN\npublic static final  int TASK_ID_UNKNOWN\nprivate  int mTaskId\nprivate  int mVirtualDisplayId\nprivate @android.view.ContentRecordingSession.RecordContent int mContentToRecord\nprivate  int mDisplayToRecord\nprivate @android.annotation.Nullable android.os.IBinder mTokenToRecord\nprivate  boolean mWaitingForConsent\nprivate  int mTargetUid\npublic static  android.view.ContentRecordingSession createDisplaySession(int)\npublic static  android.view.ContentRecordingSession createTaskSession(android.os.IBinder)\npublic static  android.view.ContentRecordingSession createTaskSession(android.os.IBinder,int)\npublic static  boolean isValid(android.view.ContentRecordingSession)\npublic static  boolean isProjectionOnSameDisplay(android.view.ContentRecordingSession,android.view.ContentRecordingSession)\nclass ContentRecordingSession extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genToString=true, genSetters=true, genEqualsHashCode=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index ba1915c..15b0c13 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -2263,6 +2263,7 @@
             this(modeId, width, height, refreshRate, vsyncRate, false, alternativeRefreshRates,
                     supportedHdrTypes);
         }
+
         /**
          * @hide
          */
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index da86e2d..8b9d876 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -213,7 +213,7 @@
 
     /**
      * The supported modes that will be exposed externally.
-     * Might have different set of modes that supportedModes for VRR displays
+     * Might have different set of modes than supportedModes for VRR displays
      */
     public Display.Mode[] appsSupportedModes = Display.Mode.EMPTY_ARRAY;
 
diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java
index beb4d95..57d1b8d 100644
--- a/core/java/android/view/HandwritingInitiator.java
+++ b/core/java/android/view/HandwritingInitiator.java
@@ -227,7 +227,10 @@
                             mState.mStylusDownY, /* isHover */ false);
                     if (candidateView != null && candidateView.isEnabled()) {
                         boolean candidateHasFocus = candidateView.hasFocus();
-                        if (shouldShowHandwritingUnavailableMessageForView(candidateView)) {
+                        if (!candidateView.isStylusHandwritingAvailable()) {
+                            mState.mShouldInitHandwriting = false;
+                            return false;
+                        } else if (shouldShowHandwritingUnavailableMessageForView(candidateView)) {
                             int messagesResId = (candidateView instanceof TextView tv
                                     && tv.isAnyPasswordInputType())
                                     ? R.string.error_handwriting_unsupported_password
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index a9846fb..eec805b7 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -303,7 +303,7 @@
     }
 
     /** Not running an animation. */
-    @VisibleForTesting(visibility = PACKAGE)
+    @VisibleForTesting
     public static final int ANIMATION_TYPE_NONE = -1;
 
     /** Running animation will show insets */
@@ -317,7 +317,7 @@
     public static final int ANIMATION_TYPE_USER = 2;
 
     /** Running animation will resize insets */
-    @VisibleForTesting(visibility = PACKAGE)
+    @VisibleForTesting
     public static final int ANIMATION_TYPE_RESIZE = 3;
 
     @Retention(RetentionPolicy.SOURCE)
@@ -1712,7 +1712,7 @@
         mImeSourceConsumer.onWindowFocusLost();
     }
 
-    @VisibleForTesting(visibility = PACKAGE)
+    @VisibleForTesting
     public @AnimationType int getAnimationType(@InsetsType int type) {
         for (int i = mRunningAnimations.size() - 1; i >= 0; i--) {
             InsetsAnimationControlRunner control = mRunningAnimations.get(i).runner;
diff --git a/core/java/android/view/InsetsFlags.java b/core/java/android/view/InsetsFlags.java
index ca8a7a8..2fa5768 100644
--- a/core/java/android/view/InsetsFlags.java
+++ b/core/java/android/view/InsetsFlags.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import static android.view.WindowInsetsController.APPEARANCE_FORCE_LIGHT_NAVIGATION_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_LIGHT_CAPTION_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
@@ -24,6 +25,7 @@
 import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS;
+import static android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND;
 import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT;
 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
 
@@ -69,7 +71,15 @@
             @ViewDebug.FlagToString(
                     mask = APPEARANCE_FORCE_LIGHT_NAVIGATION_BARS,
                     equals = APPEARANCE_FORCE_LIGHT_NAVIGATION_BARS,
-                    name = "FORCE_LIGHT_NAVIGATION_BARS")
+                    name = "FORCE_LIGHT_NAVIGATION_BARS"),
+            @ViewDebug.FlagToString(
+                    mask = APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND,
+                    equals = APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND,
+                    name = "APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND"),
+            @ViewDebug.FlagToString(
+                    mask = APPEARANCE_LIGHT_CAPTION_BARS,
+                    equals = APPEARANCE_LIGHT_CAPTION_BARS,
+                    name = "APPEARANCE_LIGHT_CAPTION_BARS")
     })
     public @Appearance int appearance;
 
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 6c670f5..fdb2a6e 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -17,7 +17,6 @@
 package android.view;
 
 import static android.view.InsetsController.ANIMATION_TYPE_NONE;
-import static android.view.InsetsController.ANIMATION_TYPE_RESIZE;
 import static android.view.InsetsController.AnimationType;
 import static android.view.InsetsController.DEBUG;
 import static android.view.InsetsSourceConsumerProto.ANIMATION_STATE;
@@ -32,7 +31,6 @@
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
@@ -181,11 +179,10 @@
                     mController.notifyVisibilityChanged();
                 }
 
-                // If there is no animation controlling the leash, make sure the visibility and the
-                // position is up-to-date.
-                final int animType = mController.getAnimationType(mType);
-                if (animType == ANIMATION_TYPE_NONE || animType == ANIMATION_TYPE_RESIZE) {
-                    applyRequestedVisibilityAndPositionToControl();
+                // If we have a new leash, make sure visibility is up-to-date, even though we
+                // didn't want to run an animation above.
+                if (mController.getAnimationType(mType) == ANIMATION_TYPE_NONE) {
+                    applyRequestedVisibilityToControl();
                 }
 
                 // Remove the surface that owned by last control when it lost.
@@ -374,27 +371,21 @@
         if (DEBUG) Log.d(TAG, "updateSource: " + newSource);
     }
 
-    private void applyRequestedVisibilityAndPositionToControl() {
-        if (mSourceControl == null) {
-            return;
-        }
-        final SurfaceControl leash = mSourceControl.getLeash();
-        if (leash == null) {
+    private void applyRequestedVisibilityToControl() {
+        if (mSourceControl == null || mSourceControl.getLeash() == null) {
             return;
         }
 
         final boolean requestedVisible = (mController.getRequestedVisibleTypes() & mType) != 0;
-        final Point surfacePosition = mSourceControl.getSurfacePosition();
         try (Transaction t = mTransactionSupplier.get()) {
             if (DEBUG) Log.d(TAG, "applyRequestedVisibilityToControl: " + requestedVisible);
             if (requestedVisible) {
-                t.show(leash);
+                t.show(mSourceControl.getLeash());
             } else {
-                t.hide(leash);
+                t.hide(mSourceControl.getLeash());
             }
             // Ensure the alpha value is aligned with the actual requested visibility.
-            t.setAlpha(leash, requestedVisible ? 1 : 0);
-            t.setPosition(leash, surfacePosition.x, surfacePosition.y);
+            t.setAlpha(mSourceControl.getLeash(), requestedVisible ? 1 : 0);
             t.apply();
         }
         onPerceptible(requestedVisible);
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index 4e5cb58..487214c 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -269,22 +269,66 @@
         public Array() {
         }
 
-        public Array(@NonNull Array other) {
-            mControls = other.mControls;
+        /**
+         * @param copyControls whether or not to make a copy of the each {@link InsetsSourceControl}
+         */
+        public Array(@NonNull Array other, boolean copyControls) {
+            setTo(other, copyControls);
         }
 
-        public Array(Parcel in) {
+        public Array(@NonNull Parcel in) {
             readFromParcel(in);
         }
 
-        public void set(@Nullable InsetsSourceControl[] controls) {
-            mControls = controls;
+        /** Updates the current Array to the given Array. */
+        public void setTo(@NonNull Array other, boolean copyControls) {
+            set(other.mControls, copyControls);
         }
 
+        /** Updates the current controls to the given controls. */
+        public void set(@Nullable InsetsSourceControl[] controls, boolean copyControls) {
+            if (controls == null || !copyControls) {
+                mControls = controls;
+                return;
+            }
+            // Make a copy of the array.
+            mControls = new InsetsSourceControl[controls.length];
+            for (int i = mControls.length - 1; i >= 0; i--) {
+                if (controls[i] != null) {
+                    mControls[i] = new InsetsSourceControl(controls[i]);
+                }
+            }
+        }
+
+        /** Gets the controls. */
         public @Nullable InsetsSourceControl[] get() {
             return mControls;
         }
 
+        /** Cleanup {@link SurfaceControl} stored in controls to prevent leak. */
+        public void release() {
+            if (mControls == null) {
+                return;
+            }
+            for (InsetsSourceControl control : mControls) {
+                if (control != null) {
+                    control.release(SurfaceControl::release);
+                }
+            }
+        }
+
+        /** Sets the given flags to all controls. */
+        public void setParcelableFlags(int parcelableFlags) {
+            if (mControls == null) {
+                return;
+            }
+            for (InsetsSourceControl control : mControls) {
+                if (control != null) {
+                    control.setParcelableFlags(parcelableFlags);
+                }
+            }
+        }
+
         @Override
         public int describeContents() {
             return 0;
diff --git a/core/java/android/view/KeyboardShortcutInfo.java b/core/java/android/view/KeyboardShortcutInfo.java
index 118b03c..3f6fd64 100644
--- a/core/java/android/view/KeyboardShortcutInfo.java
+++ b/core/java/android/view/KeyboardShortcutInfo.java
@@ -28,8 +28,8 @@
  * Information about a Keyboard Shortcut.
  */
 public final class KeyboardShortcutInfo implements Parcelable {
-    private final CharSequence mLabel;
-    private final Icon mIcon;
+    @Nullable private final CharSequence mLabel;
+    @Nullable private Icon mIcon;
     private final char mBaseCharacter;
     private final int mKeycode;
     private final int mModifiers;
@@ -116,6 +116,15 @@
     }
 
     /**
+     * Removes an icon that was previously set.
+     *
+     * @hide
+     */
+    public void clearIcon() {
+        mIcon = null;
+    }
+
+    /**
      * Returns the base keycode that, combined with the modifiers, triggers this shortcut. If the
      * base character was set instead, returns {@link KeyEvent#KEYCODE_UNKNOWN}. Valid keycodes are
      * defined as constants in {@link KeyEvent}.
@@ -165,4 +174,4 @@
             return new KeyboardShortcutInfo[size];
         }
     };
-}
\ No newline at end of file
+}
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 6db40bf..79a9f2d 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -682,7 +682,8 @@
      * <li>For a touch screen or touch pad, reports the approximate size of the contact area in
      * relation to the maximum detectable size for the device.  The value is normalized
      * to a range from 0 (smallest detectable size) to 1 (largest detectable size),
-     * although it is not a linear scale.  This value is of limited use.
+     * although it is not a linear scale.  The value of size can be used to
+     * determine fat touch events.
      * To obtain calibrated size information, use
      * {@link #AXIS_TOUCH_MAJOR} or {@link #AXIS_TOOL_MAJOR}.
      * </ul>
@@ -2795,13 +2796,8 @@
     }
 
     /**
-     * Returns the current pressure of this event for the given pointer
-     * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
-     * identifier for this index).
-     * The pressure generally
-     * ranges from 0 (no pressure at all) to 1 (normal pressure), however
-     * values higher than 1 may be generated depending on the calibration of
-     * the input device.
+     * Returns the value of {@link #AXIS_PRESSURE} for the given pointer <em>index</em>.
+     *
      * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
      * (the first pointer that is down) to {@link #getPointerCount()}-1.
      *
@@ -2812,14 +2808,8 @@
     }
 
     /**
-     * Returns a scaled value of the approximate size for the given pointer
-     * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
-     * identifier for this index).
-     * This represents some approximation of the area of the screen being
-     * pressed; the actual value in pixels corresponding to the
-     * touch is normalized with the device specific range of values
-     * and scaled to a value between 0 and 1. The value of size can be used to
-     * determine fat touch events.
+     * Returns the value of {@link #AXIS_SIZE} for the given pointer <em>index</em>.
+     *
      * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
      * (the first pointer that is down) to {@link #getPointerCount()}-1.
      *
@@ -2830,10 +2820,8 @@
     }
 
     /**
-     * Returns the length of the major axis of an ellipse that describes the touch
-     * area at the point of contact for the given pointer
-     * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
-     * identifier for this index).
+     * Returns the value of {@link #AXIS_TOUCH_MAJOR} for the given pointer <em>index</em>.
+     *
      * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
      * (the first pointer that is down) to {@link #getPointerCount()}-1.
      *
@@ -2844,10 +2832,8 @@
     }
 
     /**
-     * Returns the length of the minor axis of an ellipse that describes the touch
-     * area at the point of contact for the given pointer
-     * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
-     * identifier for this index).
+     * Returns the value of {@link #AXIS_TOUCH_MINOR} for the given pointer <em>index</em>.
+     *
      * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
      * (the first pointer that is down) to {@link #getPointerCount()}-1.
      *
@@ -2858,12 +2844,8 @@
     }
 
     /**
-     * Returns the length of the major axis of an ellipse that describes the size of
-     * the approaching tool for the given pointer
-     * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
-     * identifier for this index).
-     * The tool area represents the estimated size of the finger or pen that is
-     * touching the device independent of its actual touch area at the point of contact.
+     * Returns the value of {@link #AXIS_TOOL_MAJOR} for the given pointer <em>index</em>.
+     *
      * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
      * (the first pointer that is down) to {@link #getPointerCount()}-1.
      *
@@ -2874,12 +2856,8 @@
     }
 
     /**
-     * Returns the length of the minor axis of an ellipse that describes the size of
-     * the approaching tool for the given pointer
-     * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
-     * identifier for this index).
-     * The tool area represents the estimated size of the finger or pen that is
-     * touching the device independent of its actual touch area at the point of contact.
+     * Returns the value of {@link #AXIS_TOOL_MINOR} for the given pointer <em>index</em>.
+     *
      * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
      * (the first pointer that is down) to {@link #getPointerCount()}-1.
      *
@@ -2890,15 +2868,8 @@
     }
 
     /**
-     * Returns the orientation of the touch area and tool area in radians clockwise from vertical
-     * for the given pointer <em>index</em> (use {@link #getPointerId(int)} to find the pointer
-     * identifier for this index).
-     * An angle of 0 radians indicates that the major axis of contact is oriented
-     * upwards, is perfectly circular or is of unknown orientation.  A positive angle
-     * indicates that the major axis of contact is oriented to the right.  A negative angle
-     * indicates that the major axis of contact is oriented to the left.
-     * The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians
-     * (finger pointing fully right).
+     * Returns the value of {@link #AXIS_ORIENTATION} for the given pointer <em>index</em>.
+     *
      * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
      * (the first pointer that is down) to {@link #getPointerCount()}-1.
      *
diff --git a/core/java/android/view/NativeVectorDrawableAnimator.java b/core/java/android/view/NativeVectorDrawableAnimator.java
index b0556a3..e92bd1f 100644
--- a/core/java/android/view/NativeVectorDrawableAnimator.java
+++ b/core/java/android/view/NativeVectorDrawableAnimator.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import android.animation.Animator;
+
 /**
  * Exists just to allow for android.graphics & android.view package separation
  *
@@ -26,4 +28,7 @@
 public interface NativeVectorDrawableAnimator {
     /** @hide */
     long getAnimatorNativePtr();
+
+    /** @hide */
+    void setThreadedRendererAnimatorListener(Animator.AnimatorListener listener);
 }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1cb2765..5e3f09a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1984,9 +1984,25 @@
     public @interface ContentSensitivity {}
 
     /**
-     * Automatically determine whether a view displays sensitive content. For example, available
-     * autofill hints (or some other signal) can be used to determine if this view
-     * displays sensitive content.
+     * Content sensitivity is determined by the framework. The framework uses a heuristic to
+     * determine if this view displays sensitive content.
+     * Autofill hints i.e. {@link #getAutofillHints()}  are used in the heuristic
+     * to determine if this view should be considered as a sensitive view.
+     * <p>
+     * {@link #AUTOFILL_HINT_USERNAME},
+     * {@link #AUTOFILL_HINT_PASSWORD},
+     * {@link #AUTOFILL_HINT_CREDIT_CARD_NUMBER},
+     * {@link #AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE},
+     * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE},
+     * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY},
+     * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH},
+     * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR}
+     * are considered sensitive hints by the framework, and the list may include more hints
+     * in the future.
+     *
+     * <p> The window hosting a sensitive view will be marked as secure during an active media
+     * projection session. This would be equivalent to applying
+     * {@link android.view.WindowManager.LayoutParams#FLAG_SECURE} to the window.
      *
      * @see #getContentSensitivity()
      */
@@ -1996,6 +2012,10 @@
     /**
      * The view displays sensitive content.
      *
+     * <p> The window hosting a sensitive view will be marked as secure during an active media
+     * projection session. This would be equivalent to applying
+     * {@link android.view.WindowManager.LayoutParams#FLAG_SECURE} to the window.
+     *
      * @see #getContentSensitivity()
      */
     @FlaggedApi(FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API)
@@ -10548,9 +10568,13 @@
 
     /**
      * Sets content sensitivity mode to determine whether this view displays sensitive content
-     * (e.g. username, password etc.). The system may improve user privacy i.e. hide content
+     * (e.g. username, password etc.). The system will improve user privacy i.e. hide content
      * drawn by a sensitive view from screen sharing and recording.
      *
+     * <p> The window hosting a sensitive view will be marked as secure during an active media
+     * projection session. This would be equivalent to applying
+     * {@link android.view.WindowManager.LayoutParams#FLAG_SECURE} to the window.
+     *
      * @param mode {@link #CONTENT_SENSITIVITY_AUTO}, {@link #CONTENT_SENSITIVITY_NOT_SENSITIVE}
      *                                            or {@link #CONTENT_SENSITIVITY_SENSITIVE}
      */
@@ -10574,8 +10598,7 @@
      * {@link #setContentSensitivity(int)}.
      */
     @FlaggedApi(FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API)
-    public @ContentSensitivity
-    final int getContentSensitivity() {
+    public @ContentSensitivity final int getContentSensitivity() {
         return (mPrivateFlags4 & PFLAG4_CONTENT_SENSITIVITY_MASK)
                 >> PFLAG4_CONTENT_SENSITIVITY_SHIFT;
     }
@@ -23705,12 +23728,6 @@
                     mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
                     mPrivateFlags &= ~PFLAG_DIRTY_MASK;
 
-                    // // For VRR to vote the preferred frame rate
-                    if (sToolkitSetFrameRateReadOnlyFlagValue
-                            && sToolkitFrameRateViewEnablingReadOnlyFlagValue) {
-                        votePreferredFrameRate();
-                    }
-
                     mPrivateFlags4 |= PFLAG4_HAS_DRAWN;
 
                     // Fast path for layouts with no backgrounds
@@ -23727,6 +23744,12 @@
                         draw(canvas);
                     }
                 }
+
+                // For VRR to vote the preferred frame rate
+                if (sToolkitSetFrameRateReadOnlyFlagValue
+                        && sToolkitFrameRateViewEnablingReadOnlyFlagValue) {
+                    votePreferredFrameRate();
+                }
             } finally {
                 renderNode.endRecording();
                 setDisplayListProperties(renderNode);
@@ -34061,10 +34084,14 @@
     }
 
     private float convertVelocityToFrameRate(float velocityPps) {
+        // From UXR study, premium experience is:
+        // 1500+    dp/s: 120fps
+        // 0 - 1500 dp/s:  80fps
+        // OEMs are likely to modify this to balance battery and user experience for their
+        // specific device.
         float density = mAttachInfo.mDensity;
         float velocityDps = velocityPps / density;
-        // Choose a frame rate in increments of 10fps
-        return Math.min(MAX_FRAME_RATE, 60f + (10f * (float) Math.floor(velocityDps / 300f)));
+        return (velocityDps >= 1500f) ? MAX_FRAME_RATE : 80f;
     }
 
     /**
diff --git a/core/java/android/view/ViewAnimationHostBridge.java b/core/java/android/view/ViewAnimationHostBridge.java
index e0fae21..62b2b6c 100644
--- a/core/java/android/view/ViewAnimationHostBridge.java
+++ b/core/java/android/view/ViewAnimationHostBridge.java
@@ -16,14 +16,19 @@
 
 package android.view;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.graphics.RenderNode;
 
+import androidx.annotation.NonNull;
+
 /**
  * Maps a View to a RenderNode's AnimationHost
  *
  * @hide
  */
-public class ViewAnimationHostBridge implements RenderNode.AnimationHost {
+public class ViewAnimationHostBridge extends AnimatorListenerAdapter
+        implements RenderNode.AnimationHost {
     private final View mView;
 
     /**
@@ -34,17 +39,35 @@
     }
 
     @Override
-    public void registerAnimatingRenderNode(RenderNode animator) {
-        mView.mAttachInfo.mViewRootImpl.registerAnimatingRenderNode(animator);
+    public void registerAnimatingRenderNode(RenderNode renderNode, Animator animator) {
+        mView.mAttachInfo.mViewRootImpl.registerAnimatingRenderNode(renderNode);
+        animator.addListener(this);
     }
 
     @Override
     public void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animator) {
         mView.mAttachInfo.mViewRootImpl.registerVectorDrawableAnimator(animator);
+        animator.setThreadedRendererAnimatorListener(this);
     }
 
     @Override
     public boolean isAttached() {
         return mView.mAttachInfo != null;
     }
+
+    @Override
+    public void onAnimationStart(@NonNull Animator animation) {
+        ViewRootImpl viewRoot = mView.getViewRootImpl();
+        if (viewRoot != null) {
+            viewRoot.addThreadedRendererView(mView);
+        }
+    }
+
+    @Override
+    public void onAnimationEnd(@NonNull Animator animation) {
+        ViewRootImpl viewRoot = mView.getViewRootImpl();
+        if (viewRoot != null) {
+            viewRoot.removeThreadedRendererView(mView);
+        }
+    }
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 8e2b7f1..fd407d6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -143,7 +143,7 @@
 import android.app.ResourcesManager;
 import android.app.WindowConfiguration;
 import android.app.compat.CompatChanges;
-import android.app.servertransaction.WindowStateResizeItem;
+import android.app.servertransaction.WindowStateTransactionItem;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ClipData;
 import android.content.ClipDescription;
@@ -427,6 +427,12 @@
 
     private static final long NANOS_PER_SEC = 1000000000;
 
+    // If the ViewRootImpl has been idle for more than 750ms, clear the preferred
+    // frame rate category and frame rate.
+    private static final int IDLE_TIME_MILLIS = 750;
+
+    private static final long NANOS_PER_MILLI = 1_000_000;
+
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     static final ThreadLocal<HandlerActionQueue> sRunQueues = new ThreadLocal<HandlerActionQueue>();
 
@@ -659,6 +665,10 @@
     private int mMinusOneFrameIntervalMillis = 0;
     // VRR interval between the previous and the frame before
     private int mMinusTwoFrameIntervalMillis = 0;
+    // VRR has the invalidation idle message been posted?
+    private boolean mInvalidationIdleMessagePosted = false;
+    // VRR: List of all Views that are animating with the threaded render
+    private ArrayList<View> mThreadedRendererViews = new ArrayList();
 
     /**
      * Update the Choreographer's FrameInfo object with the timing information for the current
@@ -1184,6 +1194,8 @@
             toolkitFrameRateVelocityMappingReadOnly();
     private static boolean sToolkitEnableInvalidateCheckThreadFlagValue =
             Flags.enableInvalidateCheckThread();
+    private static boolean sSurfaceFlingerBugfixFlagValue =
+            com.android.graphics.surfaceflinger.flags.Flags.vrrBugfix24q4();
 
     static {
         sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly();
@@ -2264,6 +2276,29 @@
         requestLayout();
     }
 
+    /** Handles messages {@link #MSG_INSETS_CONTROL_CHANGED}. */
+    private void handleInsetsControlChanged(@NonNull InsetsState insetsState,
+            @NonNull InsetsSourceControl.Array activeControls) {
+        final InsetsSourceControl[] controls = activeControls.get();
+
+        if (mTranslator != null) {
+            mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
+            mTranslator.translateSourceControlsInScreenToAppWindow(controls);
+        }
+
+        // Deliver state change before control change, such that:
+        // a) When gaining control, controller can compare with server state to evaluate
+        // whether it needs to run animation.
+        // b) When loosing control, controller can restore server state by taking last
+        // dispatched state as truth.
+        mInsetsController.onStateChanged(insetsState);
+        if (mAdded) {
+            mInsetsController.onControlsChanged(controls);
+        } else {
+            activeControls.release();
+        }
+    }
+
     private final DisplayListener mDisplayListener = new DisplayListener() {
         @Override
         public void onDisplayChanged(int displayId) {
@@ -2755,11 +2790,27 @@
     public void bringChildToFront(View child) {
     }
 
+    // keep in sync with getHostVisibilityReason
     int getHostVisibility() {
         return mView != null && (mAppVisible || mForceDecorViewVisibility)
                 ? mView.getVisibility() : View.GONE;
     }
 
+    String getHostVisibilityReason() {
+        if (mView == null) {
+            return "mView is null";
+        }
+        if (!mAppVisible && !mForceDecorViewVisibility) {
+            return "!mAppVisible && !mForceDecorViewVisibility";
+        }
+        switch (mView.getVisibility()) {
+            case View.VISIBLE: return "View.VISIBLE";
+            case View.GONE: return "View.GONE";
+            case View.INVISIBLE: return "View.INVISIBLE";
+            default: return "";
+        }
+    }
+
     /**
      * Add LayoutTransition to the list of transitions to be started in the next traversal.
      * This list will be cleared after the transitions on the list are start()'ed. These
@@ -3311,6 +3362,7 @@
         int desiredWindowHeight;
 
         final int viewVisibility = getHostVisibility();
+        final String viewVisibilityReason = getHostVisibilityReason();
         final boolean viewVisibilityChanged = !mFirst
                 && (mViewVisibility != viewVisibility || mNewSurfaceNeeded
                 // Also check for possible double visibility update, which will make current
@@ -4185,7 +4237,7 @@
 
         if (!isViewVisible) {
             if (mLastTraversalWasVisible) {
-                logAndTrace("Not drawing due to not visible");
+                logAndTrace("Not drawing due to not visible. Reason=" + viewVisibilityReason);
             }
             mLastPerformTraversalsSkipDrawReason = "view_not_visible";
             if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
@@ -4261,8 +4313,13 @@
         // when the values are applicable.
         if (mDrawnThisFrame) {
             mDrawnThisFrame = false;
+            if (!mInvalidationIdleMessagePosted && sSurfaceFlingerBugfixFlagValue) {
+                mInvalidationIdleMessagePosted = true;
+                mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE, IDLE_TIME_MILLIS);
+            }
             setCategoryFromCategoryCounts();
             updateInfrequentCount();
+            updateFrameRateFromThreadedRendererViews();
             setPreferredFrameRate(mPreferredFrameRate);
             setPreferredFrameRateCategory(mPreferredFrameRateCategory);
             if (mPreferredFrameRate > 0
@@ -6499,6 +6556,8 @@
                     return "MSG_WINDOW_TOUCH_MODE_CHANGED";
                 case MSG_KEEP_CLEAR_RECTS_CHANGED:
                     return "MSG_KEEP_CLEAR_RECTS_CHANGED";
+                case MSG_CHECK_INVALIDATION_IDLE:
+                    return "MSG_CHECK_INVALIDATION_IDLE";
                 case MSG_REFRESH_POINTER_ICON:
                     return "MSG_REFRESH_POINTER_ICON";
                 case MSG_TOUCH_BOOST_TIMEOUT:
@@ -6572,24 +6631,11 @@
                     break;
                 }
                 case MSG_INSETS_CONTROL_CHANGED: {
-                    SomeArgs args = (SomeArgs) msg.obj;
-
-                    // Deliver state change before control change, such that:
-                    // a) When gaining control, controller can compare with server state to evaluate
-                    // whether it needs to run animation.
-                    // b) When loosing control, controller can restore server state by taking last
-                    // dispatched state as truth.
-                    mInsetsController.onStateChanged((InsetsState) args.arg1);
-                    InsetsSourceControl[] controls = (InsetsSourceControl[]) args.arg2;
-                    if (mAdded) {
-                        mInsetsController.onControlsChanged(controls);
-                    } else if (controls != null) {
-                        for (InsetsSourceControl control : controls) {
-                            if (control != null) {
-                                control.release(SurfaceControl::release);
-                            }
-                        }
-                    }
+                    final SomeArgs args = (SomeArgs) msg.obj;
+                    final InsetsState insetsState = (InsetsState) args.arg1;
+                    final InsetsSourceControl.Array activeControls =
+                            (InsetsSourceControl.Array) args.arg2;
+                    handleInsetsControlChanged(insetsState, activeControls);
                     args.recycle();
                     break;
                 }
@@ -6759,6 +6805,31 @@
                     mNumPausedForSync = 0;
                     scheduleTraversals();
                     break;
+                case MSG_CHECK_INVALIDATION_IDLE: {
+                    long delta;
+                    if (mIsTouchBoosting || mIsFrameRateBoosting || mInsetsAnimationRunning) {
+                        delta = 0;
+                    } else {
+                        delta = System.nanoTime() / NANOS_PER_MILLI - mLastUpdateTimeMillis;
+                    }
+                    if (delta >= IDLE_TIME_MILLIS) {
+                        mFrameRateCategoryHighCount = 0;
+                        mFrameRateCategoryHighHintCount = 0;
+                        mFrameRateCategoryNormalCount = 0;
+                        mFrameRateCategoryLowCount = 0;
+                        mPreferredFrameRate = 0;
+                        mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE;
+                        updateFrameRateFromThreadedRendererViews();
+                        setPreferredFrameRate(mPreferredFrameRate);
+                        setPreferredFrameRateCategory(mPreferredFrameRateCategory);
+                        mInvalidationIdleMessagePosted = false;
+                    } else {
+                        mInvalidationIdleMessagePosted = true;
+                        mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE,
+                                IDLE_TIME_MILLIS - delta);
+                    }
+                    break;
+                }
                 case MSG_TOUCH_BOOST_TIMEOUT:
                     /**
                      * Lower the frame rate after the boosting period (FRAME_RATE_TOUCH_BOOST_TIME).
@@ -7273,7 +7344,8 @@
             if (dispatcher.isBackGestureInProgress()) {
                 return FINISH_NOT_HANDLED;
             }
-            if (topCallback instanceof OnBackAnimationCallback) {
+            if (topCallback instanceof OnBackAnimationCallback
+                    && !(topCallback instanceof ImeBackAnimationController)) {
                 final OnBackAnimationCallback animationCallback =
                         (OnBackAnimationCallback) topCallback;
                 switch (keyEvent.getAction()) {
@@ -9783,25 +9855,9 @@
         mHandler.sendMessage(msg);
     }
 
-    private void dispatchInsetsControlChanged(InsetsState insetsState,
-            InsetsSourceControl[] activeControls) {
-        if (Binder.getCallingPid() == android.os.Process.myPid()) {
-            insetsState = new InsetsState(insetsState, true /* copySource */);
-            if (activeControls != null) {
-                for (int i = activeControls.length - 1; i >= 0; i--) {
-                    activeControls[i] = new InsetsSourceControl(activeControls[i]);
-                }
-            }
-        }
-        if (mTranslator != null) {
-            mTranslator.translateInsetsStateInScreenToAppWindow(insetsState);
-            mTranslator.translateSourceControlsInScreenToAppWindow(activeControls);
-        }
-        if (insetsState != null && insetsState.isSourceOrDefaultVisible(ID_IME, Type.ime())) {
-            ImeTracing.getInstance().triggerClientDump("ViewRootImpl#dispatchInsetsControlChanged",
-                    getInsetsController().getHost().getInputMethodManager(), null /* icProto */);
-        }
-        SomeArgs args = SomeArgs.obtain();
+    private void dispatchInsetsControlChanged(@NonNull InsetsState insetsState,
+            @NonNull InsetsSourceControl.Array activeControls) {
+        final SomeArgs args = SomeArgs.obtain();
         args.arg1 = insetsState;
         args.arg2 = activeControls;
         mHandler.obtainMessage(MSG_INSETS_CONTROL_CHANGED, args).sendToTarget();
@@ -11201,10 +11257,10 @@
         }
     }
 
-    static class W extends IWindow.Stub implements WindowStateResizeItem.ResizeListener {
+    static class W extends IWindow.Stub implements WindowStateTransactionItem.TransactionListener {
         private final WeakReference<ViewRootImpl> mViewAncestor;
         private final IWindowSession mWindowSession;
-        private boolean mIsFromResizeItem;
+        private boolean mIsFromTransactionItem;
 
         W(ViewRootImpl viewAncestor) {
             mViewAncestor = new WeakReference<ViewRootImpl>(viewAncestor);
@@ -11212,8 +11268,8 @@
         }
 
         @Override
-        public void onExecutingWindowStateResizeItem() {
-            mIsFromResizeItem = true;
+        public void onExecutingWindowStateTransactionItem() {
+            mIsFromTransactionItem = true;
         }
 
         @Override
@@ -11221,8 +11277,8 @@
                 MergedConfiguration mergedConfiguration, InsetsState insetsState,
                 boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, int syncSeqId,
                 boolean dragResizing, @Nullable ActivityWindowInfo activityWindowInfo) {
-            final boolean isFromResizeItem = mIsFromResizeItem;
-            mIsFromResizeItem = false;
+            final boolean isFromResizeItem = mIsFromTransactionItem;
+            mIsFromTransactionItem = false;
             // Although this is a AIDL method, it will only be triggered in local process through
             // either WindowStateResizeItem or WindowlessWindowManager.
             final ViewRootImpl viewAncestor = mViewAncestor.get();
@@ -11244,9 +11300,9 @@
                 return;
             }
             // The the parameters from WindowStateResizeItem are already copied.
-            final boolean needCopy =
+            final boolean needsCopy =
                     !isFromResizeItem && (Binder.getCallingPid() == Process.myPid());
-            if (needCopy) {
+            if (needsCopy) {
                 insetsState = new InsetsState(insetsState, true /* copySource */);
                 frames = new ClientWindowFrames(frames);
                 mergedConfiguration = new MergedConfiguration(mergedConfiguration);
@@ -11259,10 +11315,38 @@
         @Override
         public void insetsControlChanged(InsetsState insetsState,
                 InsetsSourceControl.Array activeControls) {
+            final boolean isFromInsetsControlChangeItem = mIsFromTransactionItem;
+            mIsFromTransactionItem = false;
             final ViewRootImpl viewAncestor = mViewAncestor.get();
-            if (viewAncestor != null) {
-                viewAncestor.dispatchInsetsControlChanged(insetsState, activeControls.get());
+            if (viewAncestor == null) {
+                if (isFromInsetsControlChangeItem) {
+                    activeControls.release();
+                }
+                return;
             }
+            if (insetsState.isSourceOrDefaultVisible(ID_IME, Type.ime())) {
+                ImeTracing.getInstance().triggerClientDump(
+                        "ViewRootImpl#dispatchInsetsControlChanged",
+                        viewAncestor.getInsetsController().getHost().getInputMethodManager(),
+                        null /* icProto */);
+            }
+            // If the UI thread is the same as the current thread that is dispatching
+            // WindowStateInsetsControlChangeItem, then it can run directly.
+            if (isFromInsetsControlChangeItem && viewAncestor.mHandler.getLooper()
+                    == ActivityThread.currentActivityThread().getLooper()) {
+                viewAncestor.handleInsetsControlChanged(insetsState, activeControls);
+                return;
+            }
+            // The parameters from WindowStateInsetsControlChangeItem are already copied.
+            final boolean needsCopy =
+                    !isFromInsetsControlChangeItem && (Binder.getCallingPid() == Process.myPid());
+            if (needsCopy) {
+                insetsState = new InsetsState(insetsState, true /* copySource */);
+                activeControls = new InsetsSourceControl.Array(
+                        activeControls, true /* copyControls */);
+            }
+
+            viewAncestor.dispatchInsetsControlChanged(insetsState, activeControls);
         }
 
         @Override
@@ -12577,6 +12661,24 @@
     }
 
     /**
+     * Views that are animating with the ThreadedRenderer don't use the normal invalidation
+     * path, so the value won't be updated through performTraversals. This reads the votes
+     * from those views.
+     */
+    private void updateFrameRateFromThreadedRendererViews() {
+        ArrayList<View> views = mThreadedRendererViews;
+        for (int i = views.size() - 1; i >= 0; i--) {
+            View view = views.get(i);
+            View.AttachInfo attachInfo = view.mAttachInfo;
+            if (attachInfo == null || attachInfo.mViewRootImpl != this) {
+                views.remove(i);
+            } else {
+                view.votePreferredFrameRate();
+            }
+        }
+    }
+
+    /**
      * Sets the mPreferredFrameRateCategory from the high, high_hint, normal, and low counts.
      */
     private void setCategoryFromCategoryCounts() {
@@ -12757,6 +12859,31 @@
     }
 
     /**
+     * Mark a View as having an active ThreadedRenderer animation. This is used for
+     * RenderNodeAnimators and AnimatedVectorDrawables. When the animation stops,
+     * {@link #removeThreadedRendererView(View)} must be called.
+     * @param view The View with the ThreadedRenderer animation that started.
+     */
+    public void addThreadedRendererView(View view) {
+        if (!mThreadedRendererViews.contains(view)) {
+            mThreadedRendererViews.add(view);
+        }
+    }
+
+    /**
+     * When a ThreadedRenderer animation ends, the View that is associated with it using
+     * {@link #addThreadedRendererView(View)} must be removed with a call to this method.
+     * @param view The View whose ThreadedRender animation has stopped.
+     */
+    public void removeThreadedRendererView(View view) {
+        mThreadedRendererViews.remove(view);
+        if (!mInvalidationIdleMessagePosted && sSurfaceFlingerBugfixFlagValue) {
+            mInvalidationIdleMessagePosted = true;
+            mHandler.sendEmptyMessageDelayed(MSG_CHECK_INVALIDATION_IDLE, IDLE_TIME_MILLIS);
+        }
+    }
+
+    /**
      * Returns {@link #INTERMITTENT_STATE_INTERMITTENT} when the ViewRootImpl has only been
      * updated intermittently, {@link #INTERMITTENT_STATE_NOT_INTERMITTENT} when it is
      * not updated intermittently, and {@link #INTERMITTENT_STATE_IN_TRANSITION} when it
@@ -12979,6 +13106,10 @@
     private void removeVrrMessages() {
         mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT);
         mHandler.removeMessages(MSG_FRAME_RATE_SETTING);
+        if (mInvalidationIdleMessagePosted && sSurfaceFlingerBugfixFlagValue) {
+            mInvalidationIdleMessagePosted = false;
+            mHandler.removeMessages(MSG_CHECK_INVALIDATION_IDLE);
+        }
     }
 
     /**
@@ -12997,7 +13128,7 @@
         mMinusOneFrameIntervalMillis = timeIntervalMillis;
 
         mLastUpdateTimeMillis = currentTimeMillis;
-        if (timeIntervalMillis + mMinusTwoFrameIntervalMillis
+        if (mThreadedRendererViews.isEmpty() && timeIntervalMillis + mMinusTwoFrameIntervalMillis
                 >= INFREQUENT_UPDATE_INTERVAL_MILLIS) {
             int infrequentUpdateCount = mInfrequentUpdateCount;
             mInfrequentUpdateCount = infrequentUpdateCount == INFREQUENT_UPDATE_COUNTS
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 86e5bea..1af9387 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -90,6 +90,19 @@
     public static final String EXTRA_VIRTUAL_STRUCTURE_TYPE =
             "android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_TYPE";
 
+
+    /**
+     * Key used for specifying the version of the view that generated the virtual structure for
+     * itself and its children
+     *
+     * For example, if the virtual structure is generated by a webview of version "104.0.5112.69",
+     * then the value should be "104.0.5112.69"
+     *
+     * @hide
+     */
+    public static final String EXTRA_VIRTUAL_STRUCTURE_VERSION_NUMBER =
+            "android.view.ViewStructure.extra.VIRTUAL_STRUCTURE_VERSION_NUMBER";
+
     /**
      * Set the identifier for this view.
      *
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 0f54940b..42bf420 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2801,6 +2801,10 @@
          * it from appearing in screenshots or from being viewed on non-secure
          * displays.
          *
+         * <p>See {@link android.view.View#setContentSensitivity(int)}, a window hosting
+         * a sensitive view will be marked as secure during media projection, preventing
+         * it from being viewed on non-secure displays and during screen share.
+         *
          * <p>See {@link android.view.Display#FLAG_SECURE} for more details about
          * secure surfaces and secure displays.
          */
diff --git a/core/java/android/view/accessibility/AccessibilityDisplayProxy.java b/core/java/android/view/accessibility/AccessibilityDisplayProxy.java
index 12e0814..1fe8180 100644
--- a/core/java/android/view/accessibility/AccessibilityDisplayProxy.java
+++ b/core/java/android/view/accessibility/AccessibilityDisplayProxy.java
@@ -302,6 +302,10 @@
                 }
 
                 @Override
+                public void onMagnificationSystemUIConnectionChanged(boolean connected) {
+                }
+
+                @Override
                 public void onMagnificationChanged(int displayId, @NonNull Region region,
                         MagnificationConfig config) {
                 }
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index ab7b226..edf3387 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -169,3 +169,13 @@
     description: "Feature flag for declaring system pinch zoom opt-out apis"
     bug: "315089687"
 }
+
+flag {
+    name: "wait_magnification_system_ui_connection_to_notify_service_connected"
+    namespace: "accessibility"
+    description: "Decide whether AccessibilityService needs to wait until magnification system ui connection is ready to trigger onServiceConnected"
+    bug: "337800504"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/core/java/android/widget/RemoteViewsService.java b/core/java/android/widget/RemoteViewsService.java
index 07d6acb..c79eac6 100644
--- a/core/java/android/widget/RemoteViewsService.java
+++ b/core/java/android/widget/RemoteViewsService.java
@@ -132,7 +132,8 @@
             RemoteViews.RemoteCollectionItems items = new RemoteViews.RemoteCollectionItems
                     .Builder().build();
             Parcel capSizeTestParcel = Parcel.obtain();
-            capSizeTestParcel.allowSquashing();
+            // restore allowSquashing to reduce the noise in error messages
+            boolean prevAllowSquashing = capSizeTestParcel.allowSquashing();
 
             try {
                 RemoteViews.RemoteCollectionItems.Builder itemsBuilder =
@@ -154,6 +155,7 @@
 
                 items = itemsBuilder.build();
             } finally {
+                capSizeTestParcel.restoreAllowSquashing(prevAllowSquashing);
                 // Recycle the parcel
                 capSizeTestParcel.recycle();
             }
diff --git a/core/java/android/window/ClientWindowFrames.java b/core/java/android/window/ClientWindowFrames.java
index 1bd921b..d5398e6 100644
--- a/core/java/android/window/ClientWindowFrames.java
+++ b/core/java/android/window/ClientWindowFrames.java
@@ -56,7 +56,16 @@
     public ClientWindowFrames() {
     }
 
-    public ClientWindowFrames(ClientWindowFrames other) {
+    public ClientWindowFrames(@NonNull ClientWindowFrames other) {
+        setTo(other);
+    }
+
+    private ClientWindowFrames(@NonNull Parcel in) {
+        readFromParcel(in);
+    }
+
+    /** Updates the current frames to the given frames. */
+    public void setTo(@NonNull ClientWindowFrames other) {
         frame.set(other.frame);
         displayFrame.set(other.displayFrame);
         parentFrame.set(other.parentFrame);
@@ -67,10 +76,6 @@
         compatScale = other.compatScale;
     }
 
-    private ClientWindowFrames(Parcel in) {
-        readFromParcel(in);
-    }
-
     /** Needed for AIDL out parameters. */
     public void readFromParcel(Parcel in) {
         frame.readFromParcel(in);
diff --git a/core/java/android/window/DisplayWindowPolicyController.java b/core/java/android/window/DisplayWindowPolicyController.java
index 8d71a8e..9cd2a71 100644
--- a/core/java/android/window/DisplayWindowPolicyController.java
+++ b/core/java/android/window/DisplayWindowPolicyController.java
@@ -23,6 +23,7 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.WindowConfiguration;
+import android.companion.virtualdevice.flags.Flags;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -66,6 +67,9 @@
     public DisplayWindowPolicyController() {
         synchronized (mSupportedWindowingModes) {
             mSupportedWindowingModes.add(WindowConfiguration.WINDOWING_MODE_FULLSCREEN);
+            if (Flags.virtualDisplayMultiWindowModeSupport()) {
+                mSupportedWindowingModes.add(WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW);
+            }
         }
     }
 
diff --git a/core/java/android/window/SnapshotDrawerUtils.java b/core/java/android/window/SnapshotDrawerUtils.java
index 29bb32e..4c8bad6 100644
--- a/core/java/android/window/SnapshotDrawerUtils.java
+++ b/core/java/android/window/SnapshotDrawerUtils.java
@@ -50,7 +50,6 @@
 import android.app.ActivityThread;
 import android.content.Context;
 import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.GraphicBuffer;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
@@ -68,6 +67,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.policy.DecorView;
+import com.android.window.flags.Flags;
 
 /**
  * Utils class to help draw a snapshot on a surface.
@@ -77,6 +77,14 @@
     private static final String TAG = "SnapshotDrawerUtils";
 
     /**
+     * Used to check if toolkitSetFrameRateReadOnly flag is enabled
+     *
+     * @hide
+     */
+    private static boolean sToolkitSetFrameRateReadOnlyFlagValue =
+            android.view.flags.Flags.toolkitSetFrameRateReadOnly();
+
+    /**
      * When creating the starting window, we use the exact same layout flags such that we end up
      * with a window with the exact same dimensions etc. However, these flags are not used in layout
      * and might cause other side effects so we exclude them.
@@ -181,7 +189,8 @@
 
             // We consider nearly matched dimensions as there can be rounding errors and the user
             // won't notice very minute differences from scaling one dimension more than the other
-            boolean aspectRatioMismatch = !isAspectRatioMatch(mFrame, mSnapshotW, mSnapshotH);
+            boolean aspectRatioMismatch = !isAspectRatioMatch(mFrame, mSnapshotW, mSnapshotH)
+                    && !Flags.drawSnapshotAspectRatioMatch();
 
             // Keep a reference to it such that it doesn't get destroyed when finalized.
             SurfaceControl childSurfaceControl = new SurfaceControl.Builder(session)
@@ -382,8 +391,8 @@
         }
         final SnapshotSurface drawSurface = new SnapshotSurface(
                 rootSurface, snapshot, lp.getTitle());
-
-        final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams;
+        final WindowManager.LayoutParams attrs = Flags.drawSnapshotAspectRatioMatch()
+                ? info.mainWindowLayoutParams : info.topOpaqueWindowLayoutParams;
         final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo;
         final ActivityManager.TaskDescription taskDescription =
                 getOrCreateTaskDescription(runningTaskInfo);
@@ -400,7 +409,8 @@
     public static WindowManager.LayoutParams createLayoutParameters(StartingWindowInfo info,
             CharSequence title, @WindowManager.LayoutParams.WindowType int windowType,
             int pixelFormat, IBinder token) {
-        final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams;
+        final WindowManager.LayoutParams attrs = Flags.drawSnapshotAspectRatioMatch()
+                ? info.mainWindowLayoutParams : info.topOpaqueWindowLayoutParams;
         final WindowManager.LayoutParams mainWindowParams = info.mainWindowLayoutParams;
         final InsetsState topWindowInsetsState = info.topOpaqueWindowInsetsState;
         if (attrs == null || mainWindowParams == null || topWindowInsetsState == null) {
@@ -437,6 +447,9 @@
         layoutParams.setFitInsetsTypes(attrs.getFitInsetsTypes());
         layoutParams.setFitInsetsSides(attrs.getFitInsetsSides());
         layoutParams.setFitInsetsIgnoringVisibility(attrs.isFitInsetsIgnoringVisibility());
+        if (sToolkitSetFrameRateReadOnlyFlagValue) {
+            layoutParams.setFrameRatePowerSavingsBalanced(false);
+        }
 
         layoutParams.setTitle(title);
         layoutParams.inputFeatures |= INPUT_FEATURE_NO_INPUT_CHANNEL;
@@ -527,7 +540,7 @@
 
         void drawStatusBarBackground(Canvas c, @Nullable Rect alreadyDrawnFrame,
                 int statusBarHeight) {
-            if (statusBarHeight > 0 && Color.alpha(mStatusBarColor) != 0
+            if (statusBarHeight > 0 && alpha(mStatusBarColor) != 0
                     && (alreadyDrawnFrame == null || c.getWidth() > alreadyDrawnFrame.right)) {
                 final int rightInset = (int) (mSystemBarInsets.right * mScale);
                 final int left = alreadyDrawnFrame != null ? alreadyDrawnFrame.right : 0;
@@ -541,7 +554,7 @@
             getNavigationBarRect(c.getWidth(), c.getHeight(), mSystemBarInsets, navigationBarRect,
                     mScale);
             final boolean visible = isNavigationBarColorViewVisible();
-            if (visible && Color.alpha(mNavigationBarColor) != 0
+            if (visible && alpha(mNavigationBarColor) != 0
                     && !navigationBarRect.isEmpty()) {
                 c.drawRect(navigationBarRect, mNavigationBarPaint);
             }
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 4b2beb9..5b99ff9 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
@@ -2,13 +2,6 @@
 container: "system"
 
 flag {
-  name: "disable_thin_letterboxing_reachability"
-  namespace: "large_screen_experiences_app_compat"
-  description: "Whether reachability is disabled in case of thin letterboxing"
-  bug: "334077350"
-}
-
-flag {
   name: "disable_thin_letterboxing_policy"
   namespace: "large_screen_experiences_app_compat"
   description: "Whether reachability is disabled in case of thin letterboxing"
@@ -80,6 +73,16 @@
 }
 
 flag {
+  name: "immersive_app_repositioning"
+  namespace: "large_screen_experiences_app_compat"
+  description: "Fix immersive apps changing size when repositioning"
+  bug: "334076352"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   name: "camera_compat_for_freeform"
   namespace: "large_screen_experiences_app_compat"
   description: "Whether to apply Camera Compat treatment to fixed-orientation apps in freeform windowing mode"
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index f08f5b8..d6f65f8 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -171,4 +171,15 @@
     description: "Actively release task snapshot memory"
     bug: "238206323"
     is_fixed_read_only: true
+}
+
+flag {
+  name: "draw_snapshot_aspect_ratio_match"
+  namespace: "windowing_frontend"
+  description: "The aspect ratio should always match when drawing snapshot"
+  bug: "341020277"
+  is_fixed_read_only: true
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
 }
\ No newline at end of file
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index 6af3d9e..9e69f89 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -147,4 +147,25 @@
     metadata {
         purpose: PURPOSE_BUGFIX
     }
-}
\ No newline at end of file
+}
+
+flag {
+    namespace: "windowing_sdk"
+    name: "insets_control_seq"
+    description: "Add seqId to InsetsControls to ensure the stale update is ignored"
+    bug: "339380439"
+    is_fixed_read_only: true
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
+    namespace: "windowing_sdk"
+     name: "move_animation_options_to_change"
+     description: "Move AnimationOptions from TransitionInfo to each Change"
+     bug: "327332488"
+     metadata {
+         purpose: PURPOSE_BUGFIX
+     }
+}
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 33b4e4a..75ddb58 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -103,6 +103,8 @@
     // The component name for the sub setting of Hearing aids in Accessibility settings
     public static final ComponentName ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME =
             new ComponentName("com.android.server.accessibility", "HearingAids");
+    public static final ComponentName ACCESSIBILITY_HEARING_AIDS_TILE_COMPONENT_NAME =
+            new ComponentName("com.android.server.accessibility", "HearingDevicesTile");
 
     public static final ComponentName COLOR_INVERSION_TILE_COMPONENT_NAME =
             new ComponentName("com.android.server.accessibility", "ColorInversionTile");
diff --git a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
index c08968d..6420620 100644
--- a/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
+++ b/core/java/com/android/internal/accessibility/common/ShortcutConstants.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.accessibility.common;
 
+import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
+import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_TILE_COMPONENT_NAME;
 import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME;
 import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_TILE_COMPONENT_NAME;
 import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME;
@@ -160,6 +162,8 @@
             DALTONIZER_COMPONENT_NAME, DALTONIZER_TILE_COMPONENT_NAME,
             ONE_HANDED_COMPONENT_NAME, ONE_HANDED_TILE_COMPONENT_NAME,
             REDUCE_BRIGHT_COLORS_COMPONENT_NAME, REDUCE_BRIGHT_COLORS_TILE_SERVICE_COMPONENT_NAME,
-            FONT_SIZE_COMPONENT_NAME, FONT_SIZE_TILE_COMPONENT_NAME
+            FONT_SIZE_COMPONENT_NAME, FONT_SIZE_TILE_COMPONENT_NAME,
+            ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME,
+            ACCESSIBILITY_HEARING_AIDS_TILE_COMPONENT_NAME
     );
 }
diff --git a/core/java/com/android/internal/graphics/ColorUtils.java b/core/java/com/android/internal/graphics/ColorUtils.java
index dff9551..f72a5ca 100644
--- a/core/java/com/android/internal/graphics/ColorUtils.java
+++ b/core/java/com/android/internal/graphics/ColorUtils.java
@@ -392,7 +392,7 @@
      * Convert RGB components to its CIE Lab representative components.
      *
      * <ul>
-     * <li>outLab[0] is L [0 ...1)</li>
+     * <li>outLab[0] is L [0 ...100)</li>
      * <li>outLab[1] is a [-128...127)</li>
      * <li>outLab[2] is b [-128...127)</li>
      * </ul>
@@ -474,7 +474,7 @@
      * 2° Standard Observer (1931).</p>
      *
      * <ul>
-     * <li>outLab[0] is L [0 ...1)</li>
+     * <li>outLab[0] is L [0 ...100)</li>
      * <li>outLab[1] is a [-128...127)</li>
      * <li>outLab[2] is b [-128...127)</li>
      * </ul>
diff --git a/core/java/com/android/internal/os/TimeoutRecord.java b/core/java/com/android/internal/os/TimeoutRecord.java
index 1f4abc1..e9a8d4b 100644
--- a/core/java/com/android/internal/os/TimeoutRecord.java
+++ b/core/java/com/android/internal/os/TimeoutRecord.java
@@ -45,7 +45,6 @@
             TimeoutKind.APP_REGISTERED,
             TimeoutKind.SHORT_FGS_TIMEOUT,
             TimeoutKind.JOB_SERVICE,
-            TimeoutKind.FGS_TIMEOUT,
     })
 
     @Retention(RetentionPolicy.SOURCE)
@@ -60,7 +59,6 @@
         int SHORT_FGS_TIMEOUT = 8;
         int JOB_SERVICE = 9;
         int APP_START = 10;
-        int FGS_TIMEOUT = 11;
     }
 
     /** Kind of timeout, e.g. BROADCAST_RECEIVER, etc. */
@@ -188,12 +186,6 @@
         return TimeoutRecord.endingNow(TimeoutKind.SHORT_FGS_TIMEOUT, reason);
     }
 
-    /** Record for a "foreground service" timeout. */
-    @NonNull
-    public static TimeoutRecord forFgsTimeout(String reason) {
-        return TimeoutRecord.endingNow(TimeoutKind.FGS_TIMEOUT, reason);
-    }
-
     /** Record for a job related timeout. */
     @NonNull
     public static TimeoutRecord forJobService(String reason) {
diff --git a/core/java/com/android/internal/os/anr/AnrLatencyTracker.java b/core/java/com/android/internal/os/anr/AnrLatencyTracker.java
index e11067d..f62ff38 100644
--- a/core/java/com/android/internal/os/anr/AnrLatencyTracker.java
+++ b/core/java/com/android/internal/os/anr/AnrLatencyTracker.java
@@ -22,7 +22,6 @@
 import static com.android.internal.util.FrameworkStatsLog.ANRLATENCY_REPORTED__ANR_TYPE__BROADCAST_OF_INTENT;
 import static com.android.internal.util.FrameworkStatsLog.ANRLATENCY_REPORTED__ANR_TYPE__CONTENT_PROVIDER_NOT_RESPONDING;
 import static com.android.internal.util.FrameworkStatsLog.ANRLATENCY_REPORTED__ANR_TYPE__EXECUTING_SERVICE;
-import static com.android.internal.util.FrameworkStatsLog.ANRLATENCY_REPORTED__ANR_TYPE__FGS_TIMEOUT;
 import static com.android.internal.util.FrameworkStatsLog.ANRLATENCY_REPORTED__ANR_TYPE__INPUT_DISPATCHING_TIMEOUT;
 import static com.android.internal.util.FrameworkStatsLog.ANRLATENCY_REPORTED__ANR_TYPE__INPUT_DISPATCHING_TIMEOUT_NO_FOCUSED_WINDOW;
 import static com.android.internal.util.FrameworkStatsLog.ANRLATENCY_REPORTED__ANR_TYPE__JOB_SERVICE;
@@ -549,8 +548,6 @@
                 return ANRLATENCY_REPORTED__ANR_TYPE__SHORT_FGS_TIMEOUT;
             case TimeoutKind.JOB_SERVICE:
                 return ANRLATENCY_REPORTED__ANR_TYPE__JOB_SERVICE;
-            case TimeoutKind.FGS_TIMEOUT:
-                return ANRLATENCY_REPORTED__ANR_TYPE__FGS_TIMEOUT;
             default:
                 return ANRLATENCY_REPORTED__ANR_TYPE__UNKNOWN_ANR_TYPE;
         }
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
deleted file mode 100644
index c6e8bf7..0000000
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ /dev/null
@@ -1,398 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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 android.graphics.Insets;
-import android.graphics.RecordingCanvas;
-import android.graphics.Rect;
-import android.graphics.RenderNode;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Looper;
-import android.view.Choreographer;
-import android.view.ThreadedRenderer;
-
-/**
- * The thread which draws a fill in background while the app is resizing in areas where the app
- * content draw is lagging behind the resize operation.
- * It starts with the creation and it ends once someone calls destroy().
- * Any size changes can be passed by a call to setTargetRect will passed to the thread and
- * executed via the Choreographer.
- * @hide
- */
-public class BackdropFrameRenderer extends Thread implements Choreographer.FrameCallback {
-
-    private DecorView mDecorView;
-
-    // This is containing the last requested size by a resize command. Note that this size might
-    // or might not have been applied to the output already.
-    private final Rect mTargetRect = new Rect();
-
-    // The render nodes for the multi threaded renderer.
-    private ThreadedRenderer mRenderer;
-    private RenderNode mFrameAndBackdropNode;
-    private RenderNode mSystemBarBackgroundNode;
-
-    private final Rect mOldTargetRect = new Rect();
-    private final Rect mNewTargetRect = new Rect();
-
-    private Choreographer mChoreographer;
-
-    // Cached size values from the last render for the case that the view hierarchy is gone
-    // during a configuration change.
-    private int mLastContentWidth;
-    private int mLastContentHeight;
-    private int mLastXOffset;
-    private int mLastYOffset;
-
-    // Whether to report when next frame is drawn or not.
-    private boolean mReportNextDraw;
-
-    private Drawable mCaptionBackgroundDrawable;
-    private Drawable mUserCaptionBackgroundDrawable;
-    private Drawable mResizingBackgroundDrawable;
-    private ColorDrawable mStatusBarColor;
-    private ColorDrawable mNavigationBarColor;
-    private boolean mOldFullscreen;
-    private boolean mFullscreen;
-    private final Rect mOldSystemBarInsets = new Rect();
-    private final Rect mSystemBarInsets = new Rect();
-    private final Rect mTmpRect = new Rect();
-
-    public BackdropFrameRenderer(DecorView decorView, ThreadedRenderer renderer, Rect initialBounds,
-            Drawable resizingBackgroundDrawable, Drawable captionBackgroundDrawable,
-            Drawable userCaptionBackgroundDrawable, int statusBarColor, int navigationBarColor,
-            boolean fullscreen, Insets systemBarInsets) {
-        setName("ResizeFrame");
-
-        mRenderer = renderer;
-        onResourcesLoaded(decorView, resizingBackgroundDrawable, captionBackgroundDrawable,
-                userCaptionBackgroundDrawable, statusBarColor, navigationBarColor);
-
-        // Create a render node for the content and frame backdrop
-        // which can be resized independently from the content.
-        mFrameAndBackdropNode = RenderNode.create("FrameAndBackdropNode", null);
-
-        mRenderer.addRenderNode(mFrameAndBackdropNode, true);
-
-        // Set the initial bounds and draw once so that we do not get a broken frame.
-        mTargetRect.set(initialBounds);
-        mFullscreen = fullscreen;
-        mOldFullscreen = fullscreen;
-        mSystemBarInsets.set(systemBarInsets.toRect());
-        mOldSystemBarInsets.set(systemBarInsets.toRect());
-
-        // Kick off our draw thread.
-        start();
-    }
-
-    void onResourcesLoaded(DecorView decorView, Drawable resizingBackgroundDrawable,
-            Drawable captionBackgroundDrawableDrawable, Drawable userCaptionBackgroundDrawable,
-            int statusBarColor, int navigationBarColor) {
-        synchronized (this) {
-            mDecorView = decorView;
-            mResizingBackgroundDrawable = resizingBackgroundDrawable != null
-                    && resizingBackgroundDrawable.getConstantState() != null
-                    ? resizingBackgroundDrawable.getConstantState().newDrawable()
-                    : null;
-            mCaptionBackgroundDrawable = captionBackgroundDrawableDrawable != null
-                    && captionBackgroundDrawableDrawable.getConstantState() != null
-                    ? captionBackgroundDrawableDrawable.getConstantState().newDrawable()
-                    : null;
-            mUserCaptionBackgroundDrawable = userCaptionBackgroundDrawable != null
-                    && userCaptionBackgroundDrawable.getConstantState() != null
-                    ? userCaptionBackgroundDrawable.getConstantState().newDrawable()
-                    : null;
-            if (mCaptionBackgroundDrawable == null) {
-                mCaptionBackgroundDrawable = mResizingBackgroundDrawable;
-            }
-            if (statusBarColor != 0) {
-                mStatusBarColor = new ColorDrawable(statusBarColor);
-                addSystemBarNodeIfNeeded();
-            } else {
-                mStatusBarColor = null;
-            }
-            if (navigationBarColor != 0) {
-                mNavigationBarColor = new ColorDrawable(navigationBarColor);
-                addSystemBarNodeIfNeeded();
-            } else {
-                mNavigationBarColor = null;
-            }
-        }
-    }
-
-    private void addSystemBarNodeIfNeeded() {
-        if (mSystemBarBackgroundNode != null) {
-            return;
-        }
-        mSystemBarBackgroundNode = RenderNode.create("SystemBarBackgroundNode", null);
-        mRenderer.addRenderNode(mSystemBarBackgroundNode, false);
-    }
-
-    /**
-     * Call this function asynchronously when the window size has been changed or when the insets
-     * have changed or whether window switched between a fullscreen or non-fullscreen layout.
-     * The change will be picked up once per frame and the frame will be re-rendered accordingly.
-     *
-     * @param newTargetBounds The new target bounds.
-     * @param fullscreen Whether the window is currently drawing in fullscreen.
-     * @param systemBarInsets The current visible system insets for the window.
-     */
-    public void setTargetRect(Rect newTargetBounds, boolean fullscreen, Rect systemBarInsets) {
-        synchronized (this) {
-            mFullscreen = fullscreen;
-            mTargetRect.set(newTargetBounds);
-            mSystemBarInsets.set(systemBarInsets);
-            // Notify of a bounds change.
-            pingRenderLocked(false /* drawImmediate */);
-        }
-    }
-
-    /**
-     * The window got replaced due to a configuration change.
-     */
-    public void onConfigurationChange() {
-        synchronized (this) {
-            if (mRenderer != null) {
-                // Enforce a window redraw.
-                mOldTargetRect.set(0, 0, 0, 0);
-                pingRenderLocked(false /* drawImmediate */);
-            }
-        }
-    }
-
-    /**
-     * All resources of the renderer will be released. This function can be called from the
-     * the UI thread as well as the renderer thread.
-     */
-    void releaseRenderer() {
-        synchronized (this) {
-            if (mRenderer != null) {
-                // Invalidate the current content bounds.
-                mRenderer.setContentDrawBounds(0, 0, 0, 0);
-
-                // Remove the render node again
-                // (see comment above - better to do that only once).
-                mRenderer.removeRenderNode(mFrameAndBackdropNode);
-                if (mSystemBarBackgroundNode != null) {
-                    mRenderer.removeRenderNode(mSystemBarBackgroundNode);
-                }
-
-                mRenderer = null;
-
-                // Exit the renderer loop.
-                pingRenderLocked(false /* drawImmediate */);
-            }
-        }
-    }
-
-    @Override
-    public void run() {
-        try {
-            Looper.prepare();
-            synchronized (this) {
-                if (mRenderer == null) {
-                    // This can happen if 'releaseRenderer' is called immediately after 'start'.
-                    return;
-                }
-                mChoreographer = Choreographer.getInstance();
-            }
-            Looper.loop();
-        } finally {
-            releaseRenderer();
-        }
-        synchronized (this) {
-            // Make sure no more messages are being sent.
-            mChoreographer = null;
-            Choreographer.releaseInstance();
-        }
-    }
-
-    /**
-     * The implementation of the FrameCallback.
-     * @param frameTimeNanos The time in nanoseconds when the frame started being rendered,
-     * in the {@link System#nanoTime()} timebase.  Divide this value by {@code 1000000}
-     */
-    @Override
-    public void doFrame(long frameTimeNanos) {
-        synchronized (this) {
-            if (mRenderer == null) {
-                reportDrawIfNeeded();
-                // Tell the looper to stop. We are done.
-                Looper.myLooper().quit();
-                return;
-            }
-            doFrameUncheckedLocked();
-        }
-    }
-
-    private void doFrameUncheckedLocked() {
-        mNewTargetRect.set(mTargetRect);
-        if (!mNewTargetRect.equals(mOldTargetRect)
-                || mOldFullscreen != mFullscreen
-                || !mSystemBarInsets.equals(mOldSystemBarInsets)
-                || mReportNextDraw) {
-            mOldFullscreen = mFullscreen;
-            mOldTargetRect.set(mNewTargetRect);
-            mOldSystemBarInsets.set(mSystemBarInsets);
-            redrawLocked(mNewTargetRect, mFullscreen);
-        }
-    }
-
-    /**
-     * The content is about to be drawn and we got the location of where it will be shown.
-     * If a "redrawLocked" call has already been processed, we will re-issue the call
-     * if the previous call was ignored since the size was unknown.
-     * @param xOffset The x offset where the content is drawn to.
-     * @param yOffset The y offset where the content is drawn to.
-     * @param xSize The width size of the content. This should not be 0.
-     * @param ySize The height of the content.
-     * @return true if a frame should be requested after the content is drawn; false otherwise.
-     */
-    boolean onContentDrawn(int xOffset, int yOffset, int xSize, int ySize) {
-        synchronized (this) {
-            final boolean firstCall = mLastContentWidth == 0;
-            // The current content buffer is drawn here.
-            mLastContentWidth = xSize;
-            mLastContentHeight = ySize;
-            mLastXOffset = xOffset;
-            mLastYOffset = yOffset;
-
-            // Inform the renderer of the content's new bounds
-            mRenderer.setContentDrawBounds(
-                    mLastXOffset,
-                    mLastYOffset,
-                    mLastXOffset + mLastContentWidth,
-                    mLastYOffset + mLastContentHeight);
-
-            // If this was the first call and redrawLocked got already called prior
-            // to us, we should re-issue a redrawLocked now.
-            return firstCall;
-        }
-    }
-
-    void onRequestDraw(boolean reportNextDraw) {
-        synchronized (this) {
-            mReportNextDraw = reportNextDraw;
-            mOldTargetRect.set(0, 0, 0, 0);
-            pingRenderLocked(true /* drawImmediate */);
-        }
-    }
-
-    /**
-     * Redraws the background, the caption and the system inset backgrounds if something changed.
-     *
-     * @param newBounds The window bounds which needs to be drawn.
-     * @param fullscreen Whether the window is currently drawing in fullscreen.
-     */
-    private void redrawLocked(Rect newBounds, boolean fullscreen) {
-
-        // Make sure that the other thread has already prepared the render draw calls for the
-        // content. If any size is 0, we have to wait for it to be drawn first.
-        if (mLastContentWidth == 0 || mLastContentHeight == 0) {
-            return;
-        }
-
-        // Content may not be drawn at the surface origin, so we want to keep the offset when we're
-        // resizing it.
-        final int left = mLastXOffset + newBounds.left;
-        final int top = mLastYOffset + newBounds.top;
-        final int width = newBounds.width();
-        final int height = newBounds.height();
-
-        mFrameAndBackdropNode.setLeftTopRightBottom(left, top, left + width, top + height);
-
-        // Draw the caption and content backdrops in to our render node.
-        RecordingCanvas canvas = mFrameAndBackdropNode.beginRecording(width, height);
-        final Drawable drawable = mUserCaptionBackgroundDrawable != null
-                ? mUserCaptionBackgroundDrawable : mCaptionBackgroundDrawable;
-
-        if (drawable != null) {
-            drawable.setBounds(0, 0, left + width, top);
-            drawable.draw(canvas);
-        }
-
-        // The backdrop: clear everything with the background. Clipping is done elsewhere.
-        if (mResizingBackgroundDrawable != null) {
-            mResizingBackgroundDrawable.setBounds(0, 0, left + width, top + height);
-            mResizingBackgroundDrawable.draw(canvas);
-        }
-        mFrameAndBackdropNode.endRecording();
-
-        drawColorViews(left, top, width, height, fullscreen);
-
-        // We need to render the node explicitly
-        mRenderer.drawRenderNode(mFrameAndBackdropNode);
-
-        reportDrawIfNeeded();
-    }
-
-    private void drawColorViews(int left, int top, int width, int height, boolean fullscreen) {
-        if (mSystemBarBackgroundNode == null) {
-            return;
-        }
-        RecordingCanvas canvas = mSystemBarBackgroundNode.beginRecording(width, height);
-        mSystemBarBackgroundNode.setLeftTopRightBottom(left, top, left + width, top + height);
-        final int topInset = mSystemBarInsets.top;
-        if (mStatusBarColor != null) {
-            mStatusBarColor.setBounds(0, 0, left + width, topInset);
-            mStatusBarColor.draw(canvas);
-        }
-
-        // We only want to draw the navigation bar if our window is currently fullscreen because we
-        // don't want the navigation bar background be moving around when resizing in docked mode.
-        // However, we need it for the transitions into/out of docked mode.
-        if (mNavigationBarColor != null && fullscreen) {
-            DecorView.getNavigationBarRect(width, height, mSystemBarInsets, mTmpRect, 1f);
-            mNavigationBarColor.setBounds(mTmpRect);
-            mNavigationBarColor.draw(canvas);
-        }
-        mSystemBarBackgroundNode.endRecording();
-        mRenderer.drawRenderNode(mSystemBarBackgroundNode);
-    }
-
-    /** Notify view root that a frame has been drawn by us, if it has requested so. */
-    private void reportDrawIfNeeded() {
-        if (mReportNextDraw) {
-            if (mDecorView.isAttachedToWindow()) {
-                mDecorView.getViewRootImpl().reportDrawFinish();
-            }
-            mReportNextDraw = false;
-        }
-    }
-
-    /**
-     * Sends a message to the renderer to wake up and perform the next action which can be
-     * either the next rendering or the self destruction if mRenderer is null.
-     * Note: This call must be synchronized.
-     *
-     * @param drawImmediate if we should draw immediately instead of scheduling a frame
-     */
-    private void pingRenderLocked(boolean drawImmediate) {
-        if (mChoreographer != null && !drawImmediate) {
-            mChoreographer.postFrameCallback(this);
-        } else {
-            doFrameUncheckedLocked();
-        }
-    }
-
-    void setUserCaptionBackgroundDrawable(Drawable userCaptionBackgroundDrawable) {
-        synchronized (this) {
-            mUserCaptionBackgroundDrawable = userCaptionBackgroundDrawable;
-        }
-    }
-}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 74c2325..c14a6c1 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -57,7 +57,6 @@
 import android.graphics.PixelFormat;
 import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
-import android.graphics.Region;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.InsetDrawable;
@@ -238,11 +237,8 @@
     private Rect mTempRect;
 
     private boolean mWindowResizeCallbacksAdded = false;
-    private Drawable.Callback mLastBackgroundDrawableCb = null;
-    private BackdropFrameRenderer mBackdropFrameRenderer = null;
     private Drawable mOriginalBackgroundDrawable;
     private Drawable mLastOriginalBackgroundDrawable;
-    private Drawable mResizingBackgroundDrawable;
     private BackgroundBlurDrawable mBackgroundBlurDrawable;
     private BackgroundBlurDrawable mLastBackgroundBlurDrawable;
 
@@ -253,8 +249,6 @@
      */
     @Nullable
     private Drawable mPendingWindowBackground;
-    private Drawable mCaptionBackgroundDrawable;
-    private Drawable mUserCaptionBackgroundDrawable;
 
     String mLogTag = TAG;
     private final Rect mFloatingInsets = new Rect();
@@ -329,26 +323,6 @@
     }
 
     @Override
-    public boolean gatherTransparentRegion(Region region) {
-        boolean statusOpaque = gatherTransparentRegion(mStatusColorViewState, region);
-        boolean navOpaque = gatherTransparentRegion(mNavigationColorViewState, region);
-        boolean decorOpaque = super.gatherTransparentRegion(region);
-
-        // combine bools after computation, so each method above always executes
-        return statusOpaque || navOpaque || decorOpaque;
-    }
-
-    boolean gatherTransparentRegion(ColorViewState colorViewState, Region region) {
-        if (colorViewState.view != null && colorViewState.visible && isResizing()) {
-            // If a visible ColorViewState is in a resizing host DecorView, forcibly register its
-            // opaque area, since it's drawn by a different root RenderNode. It would otherwise be
-            // rejected by ViewGroup#gatherTransparentRegion() for the view not being VISIBLE.
-            return colorViewState.view.gatherTransparentRegion(region);
-        }
-        return false; // no opaque area added
-    }
-
-    @Override
     public void onDraw(Canvas c) {
         super.onDraw(c);
 
@@ -838,7 +812,7 @@
         final MenuHelper helper;
         final boolean isPopup = !Float.isNaN(x) && !Float.isNaN(y);
         if (isPopup) {
-            helper = mWindow.mContextMenu.showPopup(getContext(), originalView, x, y);
+            helper = mWindow.mContextMenu.showPopup(originalView.getContext(), originalView, x, y);
         } else {
             helper = mWindow.mContextMenu.showDialog(originalView, originalView.getWindowToken());
         }
@@ -977,15 +951,11 @@
                 updateColorViews(null /* insets */, false /* animate */);
             }
             if (drawable != null) {
-                mResizingBackgroundDrawable = enforceNonTranslucentBackground(drawable,
-                        mWindow.isTranslucent() || mWindow.isShowingWallpaper());
-            } else {
-                mResizingBackgroundDrawable = getResizingBackgroundDrawable(
-                        mWindow.mBackgroundDrawable, mWindow.mBackgroundFallbackDrawable,
-                        mWindow.isTranslucent() || mWindow.isShowingWallpaper());
-            }
-            if (mResizingBackgroundDrawable != null) {
-                mResizingBackgroundDrawable.getPadding(mBackgroundPadding);
+                drawable.getPadding(mBackgroundPadding);
+            } else if (mWindow.mBackgroundDrawable != null) {
+                mWindow.mBackgroundDrawable.getPadding(mBackgroundPadding);
+            } else if (mWindow.mBackgroundFallbackDrawable != null) {
+                mWindow.mBackgroundFallbackDrawable.getPadding(mBackgroundPadding);
             } else {
                 mBackgroundPadding.setEmpty();
             }
@@ -1451,7 +1421,7 @@
                 mWindow.getAttributes().flags, force);
         boolean show = state.attributes.isVisible(state.present, color,
                 mWindow.getAttributes().flags, force);
-        boolean showView = show && !isResizing() && size > 0;
+        boolean showView = show && size > 0;
 
         boolean visibilityChanged = false;
         View view = state.view;
@@ -1505,7 +1475,7 @@
         }
         if (visibilityChanged) {
             view.animate().cancel();
-            if (animate && !isResizing()) {
+            if (animate) {
                 if (showView) {
                     if (view.getVisibility() != VISIBLE) {
                         view.setVisibility(VISIBLE);
@@ -1834,10 +1804,6 @@
             // Note that our ViewRootImpl object will not change.
             getViewRootImpl().addWindowCallbacks(this);
             mWindowResizeCallbacksAdded = true;
-        } else if (mBackdropFrameRenderer != null) {
-            // We are resizing and this call happened due to a configuration change. Tell the
-            // renderer about it.
-            mBackdropFrameRenderer.onConfigurationChange();
         }
 
         updateBackgroundBlurRadius();
@@ -1877,8 +1843,6 @@
             st.menu.close();
         }
 
-        releaseThreadedRenderer();
-
         if (mWindowResizeCallbacksAdded) {
             getViewRootImpl().removeWindowCallbacks(this);
             mWindowResizeCallbacksAdded = false;
@@ -2158,14 +2122,6 @@
     }
 
     void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
-        if (mBackdropFrameRenderer != null) {
-            loadBackgroundDrawablesIfNeeded();
-            mBackdropFrameRenderer.onResourcesLoaded(
-                    this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
-                    mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
-                    getCurrentColor(mNavigationColorViewState));
-        }
-
         final View root = inflater.inflate(layoutResource, null);
 
         // Put it below the color views.
@@ -2174,63 +2130,6 @@
         initializeElevation();
     }
 
-    private void loadBackgroundDrawablesIfNeeded() {
-        if (mResizingBackgroundDrawable == null) {
-            mResizingBackgroundDrawable = getResizingBackgroundDrawable(mWindow.mBackgroundDrawable,
-                    mWindow.mBackgroundFallbackDrawable, mWindow.isTranslucent()
-                    || mWindow.isShowingWallpaper());
-            if (mResizingBackgroundDrawable == null) {
-                // We shouldn't really get here as the background fallback should be always
-                // available since it is defaulted by the system.
-                Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow);
-            }
-        }
-        if (mCaptionBackgroundDrawable == null) {
-            mCaptionBackgroundDrawable = getContext().getDrawable(
-                    R.drawable.decor_caption_title_focused);
-        }
-        if (mResizingBackgroundDrawable != null) {
-            mLastBackgroundDrawableCb = mResizingBackgroundDrawable.getCallback();
-            mResizingBackgroundDrawable.setCallback(null);
-        }
-    }
-
-    /**
-     * Returns the color used to fill areas the app has not rendered content to yet when the
-     * user is resizing the window of an activity in multi-window mode.
-     */
-    public static Drawable getResizingBackgroundDrawable(@Nullable Drawable backgroundDrawable,
-            @Nullable Drawable fallbackDrawable, boolean windowTranslucent) {
-        if (backgroundDrawable != null) {
-            return enforceNonTranslucentBackground(backgroundDrawable, windowTranslucent);
-        }
-
-        if (fallbackDrawable != null) {
-            return enforceNonTranslucentBackground(fallbackDrawable, windowTranslucent);
-        }
-        return new ColorDrawable(Color.BLACK);
-    }
-
-    /**
-     * Enforces a drawable to be non-translucent to act as a background if needed, i.e. if the
-     * window is not translucent.
-     */
-    private static Drawable enforceNonTranslucentBackground(Drawable drawable,
-            boolean windowTranslucent) {
-        if (!windowTranslucent && drawable instanceof ColorDrawable) {
-            ColorDrawable colorDrawable = (ColorDrawable) drawable;
-            int color = colorDrawable.getColor();
-            if (Color.alpha(color) != 255) {
-                ColorDrawable copy = (ColorDrawable) colorDrawable.getConstantState().newDrawable()
-                        .mutate();
-                copy.setColor(
-                        Color.argb(255, Color.red(color), Color.green(color), Color.blue(color)));
-                return copy;
-            }
-        }
-        return drawable;
-    }
-
     void clearContentView() {
         for (int i = getChildCount() - 1; i >= 0; i--) {
             View v = getChildAt(i);
@@ -2243,21 +2142,13 @@
 
     @Override
     public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets,
-            Rect stableInsets) {
-        if (mBackdropFrameRenderer != null) {
-            mBackdropFrameRenderer.setTargetRect(newBounds, fullscreen, systemInsets);
-        }
-    }
+            Rect stableInsets) {}
 
     @Override
     public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets,
             Rect stableInsets) {
         if (mWindow.isDestroyed()) {
             // If the owner's window is gone, we should not be able to come here anymore.
-            releaseThreadedRenderer();
-            return;
-        }
-        if (mBackdropFrameRenderer != null) {
             return;
         }
         getViewRootImpl().requestInvalidateRootRenderNode();
@@ -2265,28 +2156,23 @@
 
     @Override
     public void onWindowDragResizeEnd() {
-        releaseThreadedRenderer();
         updateColorViews(null /* insets */, false);
         getViewRootImpl().requestInvalidateRootRenderNode();
     }
 
     @Override
     public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) {
-        if (mBackdropFrameRenderer == null) {
-            return false;
-        }
-        return mBackdropFrameRenderer.onContentDrawn(offsetX, offsetY, sizeX, sizeY);
+        return false;
     }
 
     @Override
     public void onRequestDraw(boolean reportNextDraw) {
-        if (mBackdropFrameRenderer != null) {
-            mBackdropFrameRenderer.onRequestDraw(reportNextDraw);
-        } else if (reportNextDraw) {
-            // If render thread is gone, just report immediately.
-            if (isAttachedToWindow()) {
-                getViewRootImpl().reportDrawFinish();
-            }
+        if (!reportNextDraw) {
+            return;
+        }
+        // If render thread is gone, just report immediately.
+        if (isAttachedToWindow()) {
+            getViewRootImpl().reportDrawFinish();
         }
     }
 
@@ -2307,25 +2193,6 @@
                 mLegacyNavigationBarBackgroundPaint);
     }
 
-    /** Release the renderer thread which is usually done when the user stops resizing. */
-    private void releaseThreadedRenderer() {
-        if (mResizingBackgroundDrawable != null && mLastBackgroundDrawableCb != null) {
-            mResizingBackgroundDrawable.setCallback(mLastBackgroundDrawableCb);
-            mLastBackgroundDrawableCb = null;
-        }
-
-        if (mBackdropFrameRenderer != null) {
-            mBackdropFrameRenderer.releaseRenderer();
-            mBackdropFrameRenderer = null;
-            // Bring the shadow back.
-            updateElevation();
-        }
-    }
-
-    private boolean isResizing() {
-        return mBackdropFrameRenderer != null;
-    }
-
     /**
      * The elevation gets set for the first time and the framework needs to be informed that
      * the surface layer gets created with the shadow size in mind.
@@ -2348,7 +2215,7 @@
         final boolean wasAdjustedForStack = mElevationAdjustedForStack;
         // Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null)
         // since the shadow is bound to the content size and not the target size.
-        if ((windowingMode == WINDOWING_MODE_FREEFORM) && !isResizing()) {
+        if (windowingMode == WINDOWING_MODE_FREEFORM) {
             elevation = hasWindowFocus() ?
                     DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
             // Add a maximum shadow height value to the top level view.
@@ -2367,16 +2234,8 @@
 
         // Don't change the elevation if we didn't previously adjust it for the stack it was in
         // or it didn't change.
-        if ((wasAdjustedForStack || mElevationAdjustedForStack)
-                && getElevation() != elevation) {
-            if (!isResizing()) {
-                mWindow.setElevation(elevation);
-            } else {
-                // Just suppress the shadow when resizing, don't adjust surface insets because it'll
-                // cause a flicker when drag resize for freeform window starts. #onContentDrawn()
-                // will compensate the offset when passing to BackdropFrameRenderer.
-                setElevation(elevation);
-            }
+        if ((wasAdjustedForStack || mElevationAdjustedForStack) && getElevation() != elevation) {
+            mWindow.setElevation(elevation);
         }
     }
 
@@ -2390,16 +2249,6 @@
                 getResources().getDisplayMetrics());
     }
 
-    /**
-     * Provide an override of the caption background drawable.
-     */
-    void setUserCaptionBackgroundDrawable(Drawable drawable) {
-        mUserCaptionBackgroundDrawable = drawable;
-        if (mBackdropFrameRenderer != null) {
-            mBackdropFrameRenderer.setUserCaptionBackgroundDrawable(drawable);
-        }
-    }
-
     private static String getTitleSuffix(WindowManager.LayoutParams params) {
         if (params == null) {
             return "";
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index a091e19..2194c89 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -4055,7 +4055,8 @@
 
     @Override
     public void setResizingCaptionDrawable(Drawable drawable) {
-        mDecor.setUserCaptionBackgroundDrawable(drawable);
+        // TODO(b/333724879): Deprecate this public API. The new caption in WM shell allows the app
+        // content to draw behind it directly if requested.
     }
 
     @Override
diff --git a/core/java/com/android/internal/policy/ScreenDecorationsUtils.java b/core/java/com/android/internal/policy/ScreenDecorationsUtils.java
index ec62839..067e5e88 100644
--- a/core/java/com/android/internal/policy/ScreenDecorationsUtils.java
+++ b/core/java/com/android/internal/policy/ScreenDecorationsUtils.java
@@ -18,6 +18,9 @@
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.util.DisplayUtils;
+import android.view.Display;
+import android.view.DisplayInfo;
 import android.view.RoundedCorners;
 
 import com.android.internal.R;
@@ -57,11 +60,31 @@
             bottomRadius = defaultRadius;
         }
 
+        // If the physical pixels are scaled, apply it here
+        float scale = getPhysicalPixelDisplaySizeRatio(context);
+        if (scale != 1f) {
+            topRadius = topRadius * scale;
+            bottomRadius = bottomRadius * scale;
+        }
+
         // Always use the smallest radius to make sure the rounded corners will
         // completely cover the display.
         return Math.min(topRadius, bottomRadius);
     }
 
+    static float getPhysicalPixelDisplaySizeRatio(Context context) {
+        DisplayInfo displayInfo = new DisplayInfo();
+        context.getDisplay().getDisplayInfo(displayInfo);
+        final Display.Mode maxDisplayMode =
+                DisplayUtils.getMaximumResolutionDisplayMode(displayInfo.supportedModes);
+        if (maxDisplayMode == null) {
+            return 1f;
+        }
+        return DisplayUtils.getPhysicalPixelDisplaySizeRatio(
+                maxDisplayMode.getPhysicalWidth(), maxDisplayMode.getPhysicalHeight(),
+                displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight());
+    }
+
     /**
      * If live rounded corners are supported on windows.
      */
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index 2f09a55..66b2a9c 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -1299,6 +1299,21 @@
                 == HardwareBuffer.USAGE_PROTECTED_CONTENT;
     }
 
+    /**
+     * Returns the luminance in 0~1. The surface control is the source of the hardware buffer,
+     * which will be used if the buffer is protected from reading.
+     */
+    public static float getBorderLuma(@NonNull HardwareBuffer hwBuffer,
+            @NonNull ColorSpace colorSpace, @NonNull SurfaceControl sourceSurfaceControl) {
+        if (hasProtectedContent(hwBuffer)) {
+            // The buffer cannot be read. Capture another buffer which excludes protected content
+            // from the source surface.
+            return getBorderLuma(sourceSurfaceControl, hwBuffer.getWidth(), hwBuffer.getHeight());
+        }
+        // Use the existing buffer directly.
+        return getBorderLuma(hwBuffer, colorSpace);
+    }
+
     /** Returns the luminance in 0~1. */
     public static float getBorderLuma(SurfaceControl surfaceControl, int w, int h) {
         final ScreenCapture.ScreenshotHardwareBuffer buffer =
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index 37b7288..42fa6ac 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -135,7 +135,7 @@
                 new DataSourceParams.Builder()
                         .setBufferExhaustedPolicy(
                                 DataSourceParams
-                                        .PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT)
+                                        .PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP)
                         .build();
         mDataSource.register(params);
         this.mViewerConfigInputStreamProvider = viewerConfigInputStreamProvider;
diff --git a/core/java/com/android/internal/util/NewlineNormalizer.java b/core/java/com/android/internal/util/NewlineNormalizer.java
deleted file mode 100644
index 0104d1f..0000000
--- a/core/java/com/android/internal/util/NewlineNormalizer.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.util;
-
-
-import java.util.regex.Pattern;
-
-/**
- * Utility class that replaces consecutive empty lines with single new line.
- * @hide
- */
-public class NewlineNormalizer {
-
-    private static final Pattern MULTIPLE_NEWLINES = Pattern.compile("\\v(\\s*\\v)?");
-
-    // Private constructor to prevent instantiation
-    private NewlineNormalizer() {}
-
-    /**
-     * Replaces consecutive newlines with a single newline in the input text.
-     */
-    public static String normalizeNewlines(String text) {
-        return MULTIPLE_NEWLINES.matcher(text).replaceAll("\n");
-    }
-}
diff --git a/core/java/com/android/internal/util/NotificationBigTextNormalizer.java b/core/java/com/android/internal/util/NotificationBigTextNormalizer.java
new file mode 100644
index 0000000..80d4095
--- /dev/null
+++ b/core/java/com/android/internal/util/NotificationBigTextNormalizer.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+
+import android.annotation.NonNull;
+import android.os.Trace;
+
+import java.util.regex.Pattern;
+
+/**
+ * Utility class that normalizes BigText style Notification content.
+ * @hide
+ */
+public class NotificationBigTextNormalizer {
+
+    private static final Pattern MULTIPLE_NEWLINES = Pattern.compile("\\v(\\s*\\v)?");
+    private static final Pattern HORIZONTAL_WHITESPACES = Pattern.compile("\\h+");
+
+    // Private constructor to prevent instantiation
+    private NotificationBigTextNormalizer() {}
+
+    /**
+     * Normalizes the given text by collapsing consecutive new lines into single one and cleaning
+     * up each line by removing zero-width characters, invisible formatting characters, and
+     * collapsing consecutive whitespace into single space.
+     */
+    @NonNull
+    public static String normalizeBigText(@NonNull String text) {
+        try {
+            Trace.beginSection("NotifBigTextNormalizer#normalizeBigText");
+            text = MULTIPLE_NEWLINES.matcher(text).replaceAll("\n");
+            text = HORIZONTAL_WHITESPACES.matcher(text).replaceAll(" ");
+            text = normalizeLines(text);
+            return text;
+        } finally {
+            Trace.endSection();
+        }
+    }
+
+    /**
+     * Normalizes lines in a text by removing zero-width characters, invisible formatting
+     * characters, and collapsing consecutive whitespace into single space.
+     *
+     * <p>
+     * This method processes the input text line by line. It eliminates zero-width
+     * characters (U+200B to U+200D, U+FEFF, U+034F), invisible formatting
+     * characters (U+2060 to U+2065, U+206A to U+206F, U+FFF9 to U+FFFB),
+     * and replaces any sequence of consecutive whitespace characters with a single space.
+     * </p>
+     *
+     * <p>
+     * Additionally, the method trims trailing whitespace from each line and removes any
+     * resulting empty lines.
+     * </p>
+     */
+    @NonNull
+    private static String normalizeLines(@NonNull String text) {
+        String[] lines = text.split("\n");
+        final StringBuilder textSB = new StringBuilder(text.length());
+        for (int i = 0; i < lines.length; i++) {
+            final String line = lines[i];
+            final StringBuilder lineSB = new StringBuilder(line.length());
+            boolean spaceSeen = false;
+            for (int j = 0; j < line.length(); j++) {
+                final char character = line.charAt(j);
+
+                // Skip ZERO WIDTH characters
+                if ((character >= '\u200B' && character <= '\u200D')
+                        || character == '\uFEFF' || character == '\u034F') {
+                    continue;
+                }
+                // Skip INVISIBLE_FORMATTING_CHARACTERS
+                if ((character >= '\u2060' && character <= '\u2065')
+                        || (character >= '\u206A' && character <= '\u206F')
+                        || (character >= '\uFFF9' && character <= '\uFFFB')) {
+                    continue;
+                }
+
+                if (isSpace(character)) {
+                    // eliminate consecutive spaces....
+                    if (!spaceSeen) {
+                        lineSB.append(" ");
+                    }
+                    spaceSeen = true;
+                } else {
+                    spaceSeen = false;
+                    lineSB.append(character);
+                }
+            }
+            // trim line.
+            final String currentLine = lineSB.toString().trim();
+
+            // don't add empty lines after trim.
+            if (currentLine.length() > 0) {
+                if (textSB.length() > 0) {
+                    textSB.append("\n");
+                }
+                textSB.append(currentLine);
+            }
+        }
+
+        return textSB.toString();
+    }
+
+    private static boolean isSpace(char ch) {
+        return ch != '\n' && Character.isSpaceChar(ch);
+    }
+}
diff --git a/core/java/com/android/internal/widget/NotificationRowIconView.java b/core/java/com/android/internal/widget/NotificationRowIconView.java
index 0f4615a..58bddae 100644
--- a/core/java/com/android/internal/widget/NotificationRowIconView.java
+++ b/core/java/com/android/internal/widget/NotificationRowIconView.java
@@ -59,7 +59,7 @@
     @Override
     protected void onFinishInflate() {
         // If showing the app icon, we don't need background or padding.
-        if (Flags.notificationsUseAppIcon()) {
+        if (Flags.notificationsUseAppIcon() || Flags.notificationsUseAppIconInRow()) {
             setPadding(0, 0, 0, 0);
             setBackground(null);
         }
diff --git a/core/jni/android_database_SQLiteRawStatement.cpp b/core/jni/android_database_SQLiteRawStatement.cpp
index b6b78811..8fc13a8 100644
--- a/core/jni/android_database_SQLiteRawStatement.cpp
+++ b/core/jni/android_database_SQLiteRawStatement.cpp
@@ -41,6 +41,11 @@
  */
 namespace android {
 
+// A zero-length byte array that can be returned by getColumnBlob().  The theory is that
+// zero-length blobs are common enough that it is worth having a single, global instance. The
+// object is created in the jni registration function.  It is never destroyed.
+static jbyteArray emptyArray = nullptr;
+
 // Helper functions.
 static sqlite3 *db(long statementPtr) {
     return sqlite3_db_handle(reinterpret_cast<sqlite3_stmt*>(statementPtr));
@@ -226,7 +231,7 @@
     throwIfInvalidColumn(env, stmtPtr, col);
     const void* blob = sqlite3_column_blob(stmt(stmtPtr), col);
     if (blob == nullptr) {
-        return NULL;
+        return (sqlite3_column_type(stmt(stmtPtr), col) == SQLITE_NULL) ? NULL : emptyArray;
     }
     size_t size = sqlite3_column_bytes(stmt(stmtPtr), col);
     jbyteArray result = env->NewByteArray(size);
@@ -316,8 +321,10 @@
 
 int register_android_database_SQLiteRawStatement(JNIEnv *env)
 {
-    return RegisterMethodsOrDie(env, "android/database/sqlite/SQLiteRawStatement",
-                                sStatementMethods, NELEM(sStatementMethods));
+    RegisterMethodsOrDie(env, "android/database/sqlite/SQLiteRawStatement",
+                         sStatementMethods, NELEM(sStatementMethods));
+    emptyArray = MakeGlobalRefOrDie(env, env->NewByteArray(0));
+    return 0;
 }
 
 } // namespace android
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index d48cdc4..eaff760 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -713,6 +713,19 @@
             AudioSystem::getForceUse(static_cast<audio_policy_force_use_t>(usage)));
 }
 
+static jint android_media_AudioSystem_setDeviceAbsoluteVolumeEnabled(JNIEnv *env, jobject thiz,
+                                                                     jint device, jstring address,
+                                                                     jboolean enabled,
+                                                                     jint stream) {
+    const char *c_address = env->GetStringUTFChars(address, nullptr);
+    int state = check_AudioSystem_Command(
+            AudioSystem::setDeviceAbsoluteVolumeEnabled(static_cast<audio_devices_t>(device),
+                                                        c_address, enabled,
+                                                        static_cast<audio_stream_type_t>(stream)));
+    env->ReleaseStringUTFChars(address, c_address);
+    return state;
+}
+
 static jint
 android_media_AudioSystem_initStreamVolume(JNIEnv *env, jobject thiz, jint stream, jint indexMin, jint indexMax)
 {
@@ -3373,6 +3386,7 @@
          MAKE_AUDIO_SYSTEM_METHOD(setPhoneState),
          MAKE_AUDIO_SYSTEM_METHOD(setForceUse),
          MAKE_AUDIO_SYSTEM_METHOD(getForceUse),
+         MAKE_AUDIO_SYSTEM_METHOD(setDeviceAbsoluteVolumeEnabled),
          MAKE_AUDIO_SYSTEM_METHOD(initStreamVolume),
          MAKE_AUDIO_SYSTEM_METHOD(setStreamVolumeIndex),
          MAKE_AUDIO_SYSTEM_METHOD(getStreamVolumeIndex),
diff --git a/core/jni/android_tracing_PerfettoProducer.cpp b/core/jni/android_tracing_PerfettoProducer.cpp
index f8c63c8..f553380 100644
--- a/core/jni/android_tracing_PerfettoProducer.cpp
+++ b/core/jni/android_tracing_PerfettoProducer.cpp
@@ -34,15 +34,17 @@
 
 namespace android {
 
-void perfettoProducerInit(JNIEnv* env, jclass clazz, int backends) {
+void perfettoProducerInit(JNIEnv* env, jclass clazz, PerfettoBackendTypes backends,
+                          uint32_t shmem_size_hint_kb) {
     struct PerfettoProducerInitArgs args = PERFETTO_PRODUCER_INIT_ARGS_INIT();
-    args.backends = (PerfettoBackendTypes)backends;
+    args.backends = backends;
+    args.shmem_size_hint_kb = shmem_size_hint_kb;
     PerfettoProducerInit(args);
 }
 
 const JNINativeMethod gMethods[] = {
         /* name, signature, funcPtr */
-        {"nativePerfettoProducerInit", "(I)V", (void*)perfettoProducerInit},
+        {"nativePerfettoProducerInit", "(II)V", (void*)perfettoProducerInit},
 };
 
 int register_android_tracing_PerfettoProducer(JNIEnv* env) {
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 3006e20..2068bd7 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -1411,10 +1411,8 @@
         return JNI_TRUE;
     }
 
-    if (err == FAILED_TRANSACTION) {
-        env->CallStaticVoidMethod(gBinderOffsets.mClass, gBinderOffsets.mTransactionCallback,
-                                  getpid(), code, flags, err);
-    }
+    env->CallStaticVoidMethod(gBinderOffsets.mClass, gBinderOffsets.mTransactionCallback, getpid(),
+                              code, flags, err);
 
     if (err == UNKNOWN_TRANSACTION) {
         return JNI_FALSE;
diff --git a/core/jni/com_android_internal_content_FileSystemUtils.cpp b/core/jni/com_android_internal_content_FileSystemUtils.cpp
index 31f4e64..d426f12 100644
--- a/core/jni/com_android_internal_content_FileSystemUtils.cpp
+++ b/core/jni/com_android_internal_content_FileSystemUtils.cpp
@@ -88,7 +88,7 @@
         ALOGD("Total number of LOAD segments %zu", programHeaders.size());
 
         ALOGD("Size before punching holes st_blocks: %" PRIu64
-              ", st_blksize: %ld, st_size: %" PRIu64 "",
+              ", st_blksize: %d, st_size: %" PRIu64 "",
               beforePunch.st_blocks, beforePunch.st_blksize,
               static_cast<uint64_t>(beforePunch.st_size));
     }
@@ -193,7 +193,7 @@
             ALOGD("lstat64 failed for filePath %s, error:%d", filePath, errno);
             return false;
         }
-        ALOGD("Size after punching holes st_blocks: %" PRIu64 ", st_blksize: %ld, st_size: %" PRIu64
+        ALOGD("Size after punching holes st_blocks: %" PRIu64 ", st_blksize: %d, st_size: %" PRIu64
               "",
               afterPunch.st_blocks, afterPunch.st_blksize,
               static_cast<uint64_t>(afterPunch.st_size));
@@ -271,7 +271,7 @@
     uint64_t blockSize = beforePunch.st_blksize;
     IF_ALOGD() {
         ALOGD("Extra field length: %hu,  Size before punching holes st_blocks: %" PRIu64
-              ", st_blksize: %ld, st_size: %" PRIu64 "",
+              ", st_blksize: %d, st_size: %" PRIu64 "",
               extraFieldLen, beforePunch.st_blocks, beforePunch.st_blksize,
               static_cast<uint64_t>(beforePunch.st_size));
     }
@@ -346,7 +346,7 @@
             return false;
         }
         ALOGD("punchHolesInApk:: Size after punching holes st_blocks: %" PRIu64
-              ", st_blksize: %ld, st_size: %" PRIu64 "",
+              ", st_blksize: %d, st_size: %" PRIu64 "",
               afterPunch.st_blocks, afterPunch.st_blksize,
               static_cast<uint64_t>(afterPunch.st_size));
     }
diff --git a/core/proto/android/app/appexitinfo.proto b/core/proto/android/app/appexitinfo.proto
index 3abc462..e560a94 100644
--- a/core/proto/android/app/appexitinfo.proto
+++ b/core/proto/android/app/appexitinfo.proto
@@ -20,7 +20,7 @@
 package android.app;
 
 import "frameworks/base/core/proto/android/privacy.proto";
-import "frameworks/proto_logging/stats/enums/app/enums.proto";
+import "frameworks/proto_logging/stats/enums/app/app_enums.proto";
 
 /**
  * An android.app.ApplicationExitInfo object.
diff --git a/core/proto/android/app/appstartinfo.proto b/core/proto/android/app/appstartinfo.proto
index d9ed911..c137533 100644
--- a/core/proto/android/app/appstartinfo.proto
+++ b/core/proto/android/app/appstartinfo.proto
@@ -20,7 +20,7 @@
 package android.app;
 
 import "frameworks/base/core/proto/android/privacy.proto";
-import "frameworks/proto_logging/stats/enums/app/enums.proto";
+import "frameworks/proto_logging/stats/enums/app/app_enums.proto";
 
 /**
  * An android.app.ApplicationStartInfo object.
diff --git a/core/proto/android/os/batterystats.proto b/core/proto/android/os/batterystats.proto
index 4c84944..97f8148 100644
--- a/core/proto/android/os/batterystats.proto
+++ b/core/proto/android/os/batterystats.proto
@@ -21,7 +21,7 @@
 
 import "frameworks/base/core/proto/android/os/powermanager.proto";
 import "frameworks/base/core/proto/android/privacy.proto";
-import "frameworks/proto_logging/stats/enums/app/job/enums.proto";
+import "frameworks/proto_logging/stats/enums/app/job/job_enums.proto";
 import "frameworks/proto_logging/stats/enums/telephony/enums.proto";
 
 message BatteryStatsProto {
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index e3a438d..921c41c 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -35,7 +35,7 @@
 import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
 import "frameworks/base/core/proto/android/util/common.proto";
 import "frameworks/base/core/proto/android/privacy.proto";
-import "frameworks/proto_logging/stats/enums/app/enums.proto";
+import "frameworks/proto_logging/stats/enums/app/app_enums.proto";
 
 option java_multiple_files = true;
 
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 00127c1..a1e3dc1 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -31,7 +31,7 @@
 import "frameworks/base/core/proto/android/server/statlogger.proto";
 import "frameworks/base/core/proto/android/privacy.proto";
 import "frameworks/base/core/proto/android/util/quotatracker.proto";
-import "frameworks/proto_logging/stats/enums/app/job/enums.proto";
+import "frameworks/proto_logging/stats/enums/app/job/job_enums.proto";
 import "frameworks/proto_logging/stats/enums/server/job/enums.proto";
 
 message JobSchedulerServiceDumpProto {
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index 2f865af..593bbc6 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -26,7 +26,7 @@
 import "frameworks/base/core/proto/android/providers/settings.proto";
 import "frameworks/base/core/proto/android/server/wirelesschargerdetector.proto";
 import "frameworks/base/core/proto/android/privacy.proto";
-import "frameworks/proto_logging/stats/enums/app/enums.proto";
+import "frameworks/proto_logging/stats/enums/app/app_enums.proto";
 import "frameworks/proto_logging/stats/enums/os/enums.proto";
 import "frameworks/proto_logging/stats/enums/view/enums.proto";
 
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7b9235cd..6dbe44b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4702,6 +4702,11 @@
     <permission android:name="android.permission.REQUEST_UNIQUE_ID_ATTESTATION"
          android:protectionLevel="signature" />
 
+    <!-- Allows an application to use the RemoteKeyProvisioningService.
+         @hide -->
+    <permission android:name="android.permission.BIND_RKP_SERVICE"
+                android:protectionLevel="signature" />
+
     <!-- Allows an application to get enabled credential manager providers.
          @hide -->
     <permission android:name="android.permission.LIST_ENABLED_CREDENTIAL_PROVIDERS"
diff --git a/core/res/res/drawable/decor_caption_title.xml b/core/res/res/drawable/decor_caption_title.xml
deleted file mode 100644
index 591605d3..0000000
--- a/core/res/res/drawable/decor_caption_title.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_window_focused="true"
-          android:drawable="@drawable/decor_caption_title_focused" />
-    <item android:drawable="@drawable/decor_caption_title_unfocused" />
-</selector>
diff --git a/core/res/res/drawable/decor_caption_title_focused.xml b/core/res/res/drawable/decor_caption_title_focused.xml
deleted file mode 100644
index 7d1c230..0000000
--- a/core/res/res/drawable/decor_caption_title_focused.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<shape android:shape="rectangle"
-       android:tintMode="multiply"
-       android:tint="#D8D8D8"
-       xmlns:android="http://schemas.android.com/apk/res/android">
-     <!-- Fading the primary color to 85% blackness -->
-     <solid android:color="?android:attr/colorPrimary" />
-</shape>
diff --git a/core/res/res/drawable/decor_caption_title_unfocused.xml b/core/res/res/drawable/decor_caption_title_unfocused.xml
deleted file mode 100644
index 2846d8ca..0000000
--- a/core/res/res/drawable/decor_caption_title_unfocused.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<shape android:shape="rectangle"
-       android:tintMode="multiply"
-       android:tint="#F2F2F2"
-       xmlns:android="http://schemas.android.com/apk/res/android">
-     <!-- Fading the primary color to 95% blackness -->
-     <solid android:color="?android:attr/colorPrimary"/>
-</shape>
diff --git a/core/res/res/drawable/decor_close_button_dark.xml b/core/res/res/drawable/decor_close_button_dark.xml
deleted file mode 100644
index 950e4fd..0000000
--- a/core/res/res/drawable/decor_close_button_dark.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
-Copyright (C) 2015 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT 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="32.0dp"
-        android:height="32.0dp"
-        android:viewportWidth="32.0"
-        android:viewportHeight="32.0"
-        android:tint="@color/decor_button_dark_color"
-        >
-    <group android:scaleX="0.5"
-            android:scaleY="0.5"
-            android:translateX="8.0"
-            android:translateY="8.0" >
-        <path
-            android:fillColor="@color/white"
-            android:pathData="M6.9,4.0l-2.9,2.9 9.1,9.1 -9.1,9.200001 2.9,2.799999 9.1,-9.1 9.1,9.1 2.9,-2.799999 -9.1,-9.200001 9.1,-9.1 -2.9,-2.9 -9.1,9.2z"/>
-    </group>
-</vector>
diff --git a/core/res/res/drawable/decor_close_button_light.xml b/core/res/res/drawable/decor_close_button_light.xml
deleted file mode 100644
index d75cd25..0000000
--- a/core/res/res/drawable/decor_close_button_light.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<!--
-Copyright (C) 2015 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT 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="32.0dp"
-        android:height="32.0dp"
-        android:viewportWidth="32.0"
-        android:viewportHeight="32.0"
-        android:tint="@color/decor_button_light_color"
-        >
-    <group android:scaleX="0.5"
-            android:scaleY="0.5"
-            android:translateX="8.0"
-            android:translateY="8.0" >
-        <path
-                android:fillColor="@color/white"
-                android:pathData="M6.9,4.0l-2.9,2.9 9.1,9.1 -9.1,9.200001 2.9,2.799999 9.1,-9.1 9.1,9.1 2.9,-2.799999 -9.1,-9.200001 9.1,-9.1 -2.9,-2.9 -9.1,9.2z"/>
-    </group>
-</vector>
diff --git a/core/res/res/drawable/decor_maximize_button_dark.xml b/core/res/res/drawable/decor_maximize_button_dark.xml
deleted file mode 100644
index 619b460..0000000
--- a/core/res/res/drawable/decor_maximize_button_dark.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT 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="32.0dp"
-        android:height="32.0dp"
-        android:viewportWidth="32.0"
-        android:viewportHeight="32.0"
-        android:tint="@color/decor_button_dark_color"
-        >
-    <group android:scaleX="0.5"
-            android:scaleY="0.5"
-            android:translateX="8.0"
-            android:translateY="8.0" >
-        <path
-            android:fillColor="@color/white"
-            android:pathData="M2.0,4.0l0.0,16.0l28.0,0.0L30.0,4.0L2.0,4.0zM26.0,16.0L6.0,16.0L6.0,8.0l20.0,0.0L26.0,16.0z"/>
-        <path
-            android:fillColor="@color/white"
-            android:pathData="M2.0,24.0l28.0,0.0l0.0,4.0l-28.0,0.0z"/>
-    </group>
-</vector>
-
-
diff --git a/core/res/res/drawable/decor_maximize_button_light.xml b/core/res/res/drawable/decor_maximize_button_light.xml
deleted file mode 100644
index 5b55fd2..0000000
--- a/core/res/res/drawable/decor_maximize_button_light.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<!--
-Copyright (C) 2015 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT 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="32.0dp"
-        android:height="32.0dp"
-        android:viewportWidth="32.0"
-        android:viewportHeight="32.0"
-        android:tint="@color/decor_button_light_color"
-        >
-    <group android:scaleX="0.5"
-            android:scaleY="0.5"
-            android:translateX="8.0"
-            android:translateY="8.0" >
-        <path
-            android:fillColor="@color/white"
-            android:pathData="M2.0,4.0l0.0,16.0l28.0,0.0L30.0,4.0L2.0,4.0zM26.0,16.0L6.0,16.0L6.0,8.0l20.0,0.0L26.0,16.0z"/>
-        <path
-            android:fillColor="@color/white"
-            android:pathData="M2.0,24.0l28.0,0.0l0.0,4.0l-28.0,0.0z"/>
-    </group>
-</vector>
diff --git a/core/res/res/layout/decor_caption.xml b/core/res/res/layout/decor_caption.xml
deleted file mode 100644
index 0246736..0000000
--- a/core/res/res/layout/decor_caption.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2015, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<com.android.internal.widget.DecorCaptionView xmlns:android="http://schemas.android.com/apk/res/android"
-        android:orientation="vertical"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:descendantFocusability="beforeDescendants" >
-    <LinearLayout
-            android:id="@+id/caption"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:gravity="end"
-            android:background="@drawable/decor_caption_title"
-            android:focusable="false"
-            android:descendantFocusability="blocksDescendants" >
-        <Button
-                android:id="@+id/maximize_window"
-                android:layout_width="32dp"
-                android:layout_height="32dp"
-                android:layout_margin="5dp"
-                android:padding="4dp"
-                android:layout_gravity="center_vertical|end"
-                android:contentDescription="@string/maximize_button_text"
-                android:background="@drawable/decor_maximize_button_dark" />
-        <Button
-                android:id="@+id/close_window"
-                android:layout_width="32dp"
-                android:layout_height="32dp"
-                android:layout_margin="5dp"
-                android:padding="4dp"
-                android:layout_gravity="center_vertical|end"
-                android:contentDescription="@string/close_button_text"
-                android:background="@drawable/decor_close_button_dark" />
-    </LinearLayout>
-</com.android.internal.widget.DecorCaptionView>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 7fe8641..ca6a384 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Jou werkprofiel is nie meer op hierdie toestel beskikbaar nie"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Te veel wagwoordpogings"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administrateur het toestel vir persoonlike gebruik afgestaan"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Privaat ruimte is verwyder"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Jou organisasie laat nie privaat ruimtes op hierdie bestuurde toestel toe nie."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Toestel word bestuur"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Jou organisasie bestuur hierdie toestel en kan netwerkverkeer monitor. Tik vir besonderhede."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Programme kan toegang tot jou ligging kry"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Stembystand"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Snelsluit"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Antwoord"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Nuwe kennisgewing"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fisieke sleutelbord"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Sekuriteit"</string>
@@ -2195,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"D-paneel links"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"D-paneel regs"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"D-paneel middel"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> se onderskrifbalk."</string>
     <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>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Hoe dit werk"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Hangend …"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Stel Vingerafdrukslot weer op"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> het nie goed gewerk nie en is uitgevee"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> en <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> het nie goed gewerk nie en is uitgevee"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> het nie goed gewerk nie en is uitgevee. Stel dit weer op om jou foon met vingerafdruk te ontsluit."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> en <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> het nie goed gewerk nie en is uitgevee. Stel dit weer op om jou foon met jou vingerafdruk te ontsluit."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Stel Gesigslot weer op"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 1d1ded6..218b13a 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"የሥራ መገለጫዎ ከዚህ በኋላ በዚህ መሣሪያ ላይ አይገኝም"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"በጣም ብዙ የይለፍ ቃል ሙከራዎች"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"አስተዳዳሪ መሣሪያዎን ለግል ጥቅም ትተውታል"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"የግል ቦታ ተወግዷል"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"ድርጅትዎ የግል ቦታዎችን በዚህ የሚተዳደር መሣሪያ ላይ አይፈቅድም።"</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"መሣሪያው የሚተዳደር ነው"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"የእርስዎ ድርጅት ይህን መሣሪያ ያስተዳድራል፣ እና የአውታረ መረብ ትራፊክን ሊከታተል ይችላል። ዝርዝሮችን ለማግኘት መታ ያድርጉ።"</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"መተግበሪያዎች የእርስዎን አካባቢ መድረስ ይችላሉ"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"የድምጽ እርዳታ"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"መቆለፊያ"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"መልስ"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"አዲስ ማሳወቂያ"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"አካላዊ ቁልፍ ሰሌዳ"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"ደህንነት"</string>
@@ -2195,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"ከDpad በስተግራ"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"ከDpad በስተቀኝ"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"የDpad ማዕከል"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"የ<xliff:g id="APP_NAME">%1$s</xliff:g> የሥዕል ገላጭ ጽሁፍ አሞሌ።"</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>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"እንዴት እንደሚሠራ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"በመጠባበቅ ላይ..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"በጣት አሻራ መክፈቻን እንደገና ያዋቅሩ"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> በደንብ እየሠራ አልነበረም እና ተሰርዟል"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> እና <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> በደንብ እየሠሩ አልነበረም እና ተሰርዘዋል"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> በደንብ እየሠራ አልነበረም እና ተሰርዟል። ስልክዎን በጣት አሻራ ለመክፈት እንደገና ያዋቅሩት።"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> እና <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> በደንብ እየሠሩ አልነበረም እና ተሰርዘዋል። ስልክዎን በጣት አሻራ ለመክፈት እንደገና ያዋቅሯቸው።"</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"በመልክ መክፈትን እንደገና ያዋቅሩ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 3a4a79a..d0e28b7 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -205,6 +205,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"لم يعد ملفك الشخصي للعمل متاحًا على هذا الجهاز"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"تم إجراء محاولات كثيرة جدًا لإدخال كلمة المرور"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"تنازل المشرف عن الجهاز للاستخدام الشخصي"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"تمت إزالة المساحة الخاصّة"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"لا تسمح مؤسستك بالمساحات الخاصة على هذا الجهاز المُدار."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"تتم إدارة الجهاز"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"تدير مؤسستك هذا الجهاز ويمكنها مراقبة حركة بيانات الشبكة. يمكنك النقر للحصول على تفاصيل."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"يمكن للتطبيقات الوصول إلى موقعك الجغرافي"</string>
@@ -2198,7 +2200,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"زرّ الاتجاه لليسار"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"زرّ الاتجاه لليمين"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"الزرّ المركزي"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"شريط الشرح لتطبيق <xliff:g id="APP_NAME">%1$s</xliff:g>."</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>
@@ -2419,10 +2420,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"طريقة العمل"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"بانتظار الإزالة من الأرشيف…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"إعادة إعداد ميزة \"فتح الجهاز ببصمة الإصبع\""</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"هناك مشكلة في <xliff:g id="FINGERPRINT">%s</xliff:g> وتم حذفها"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"هناك مشكلة في <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> و<xliff:g id="FINGERPRINT_1">%2$s</xliff:g> وتم حذفهما"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"هناك مشكلة في <xliff:g id="FINGERPRINT">%s</xliff:g> وتم حذفها. يُرجى إعدادها مرة أخرى لفتح قفل هاتفك باستخدام بصمة الإصبع."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"هناك مشكلة في <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> و<xliff:g id="FINGERPRINT_1">%2$s</xliff:g> وتم حذفهما. يُرجى إعادة إعدادهما لفتح قفل هاتفك باستخدام بصمة الإصبع."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"إعادة إعداد ميزة \"فتح الجهاز بالتعرّف على الوجه\""</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index e8346b7..42b0af9 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"আপোনাৰ কৰ্মস্থানৰ প্ৰ\'ফাইল এই ডিভাইচটোত আৰু উপলব্ধ নহয়"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"বহুতবাৰ ভুলকৈ পাছৱৰ্ড দিয়া হৈছে"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"প্ৰশাসকে ডিভাইচটো ব্যক্তিগত ব্যৱহাৰৰ বাবে বাজেয়প্ত কৰিছে"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"প্ৰাইভেট স্পে’চ আঁতৰোৱা হৈছে"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"আপোনাৰ প্ৰতিষ্ঠানে এই পৰিচালিত ডিভাইচত প্ৰাইভেট স্পে’চৰ অনুমতি নিদিয়ে।"</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"পৰিচালিত ডিভাইচ"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"আপোনাৰ প্ৰতিষ্ঠানটোৱে এই ডিভাইচটো পৰিচালনা কৰে আৰু ই নেটৱৰ্কৰ ট্ৰেফিক পৰ্যবেক্ষণ কৰিব পাৰে। সবিশেষ জানিবলৈ টিপক।"</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"এপ্‌সমূহে আপোনাৰ অৱস্থান এক্সেছ কৰিব পাৰে"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"ডিপেডৰ বাওঁফালৰ বুটাম"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"ডিপেডৰ সোঁফালৰ বুটাম"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"ডিপেডৰ মাজৰ বুটাম"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g>ৰ কেপশ্বন বাৰ।"</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>
@@ -2395,7 +2396,7 @@
     <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"কীব’ৰ্ডৰ লে’আউট <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> হিচাপে ছেট কৰা হৈছে… সলনি কৰিবলৈ টিপক।"</string>
     <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"ভৌতিক কীব’ৰ্ড কনফিগাৰ কৰা হৈছে"</string>
     <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"কীব’ৰ্ড চাবলৈ টিপক"</string>
-    <string name="profile_label_private" msgid="6463418670715290696">"ব্যক্তিগত"</string>
+    <string name="profile_label_private" msgid="6463418670715290696">"প্ৰাইভেট"</string>
     <string name="profile_label_clone" msgid="769106052210954285">"ক্ল’ন"</string>
     <string name="profile_label_work" msgid="3495359133038584618">"কৰ্মস্থান"</string>
     <string name="profile_label_work_2" msgid="4691533661598632135">"কৰ্মস্থান ২"</string>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ই কেনেকৈ কাম কৰে"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"বিবেচনাধীন হৈ আছে..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ফিংগাৰপ্ৰিণ্ট আনলক পুনৰ ছেট আপ কৰক"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g>এ ভালদৰে কাম কৰা নাছিল আৰু সেইটো মচি পেলোৱা হৈছে"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> আৰু <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>এ ভালদৰে কাম কৰা নাছিল আৰু সেয়া মচি পেলোৱা হৈছে"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g>এ ভালদৰে কাম কৰা নাছিল আৰু সেইটো মচি পেলোৱা হৈছে। ফিংগাৰপ্ৰিণ্টৰ জৰিয়তে আপোনাৰ ফ’নটো আনলক কৰিবলৈ এইটো পুনৰ ছেট আপ কৰক।"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> আৰু <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>এ ভালদৰে কাম কৰা নাছিল আৰু সেয়া মচি পেলোৱা হৈছে। ফিংগাৰপ্ৰিণ্টৰ জৰিয়তে আপোনাৰ ফ’নটো আনলক কৰিবলৈ সেয়া পুনৰ ছেট আপ কৰক।"</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"ফে’চ আনলক পুনৰ ছেট আপ কৰক"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index 5f9e9fa..5633c06 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"İş profili artıq bu cihazda əlçatan deyil"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Həddindən çox parol cəhdi"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Admin şəxsi istifadə üçün cihazdan imtina etdi"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Şəxsi sahə silindi"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Təşkilat bu idarə olunan cihazda şəxsi sahələrə icazə vermir."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Cihaz idarə olunur"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Təşkilat bu cihazı idarə edir və şəbəkənin ötürülməsinə nəzarət edə bilər. Detallar üçün klikləyin."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Tətbiqlər məkanınıza daxil ola bilər"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad Sola"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad Sağa"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad Mərkəzə"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> başlıq 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>
@@ -2395,7 +2396,7 @@
     <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Klaviatura düzəni <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> kimi ayarlanıb… Dəyişmək üçün toxunun."</string>
     <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Fiziki klaviaturalar konfiqurasiya edilib"</string>
     <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Klaviaturalara baxmaq üçün toxunun"</string>
-    <string name="profile_label_private" msgid="6463418670715290696">"Şəxsi"</string>
+    <string name="profile_label_private" msgid="6463418670715290696">"Məxfi"</string>
     <string name="profile_label_clone" msgid="769106052210954285">"Klon"</string>
     <string name="profile_label_work" msgid="3495359133038584618">"İş"</string>
     <string name="profile_label_work_2" msgid="4691533661598632135">"İş 2"</string>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Haqqında"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Gözləmədə..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Barmaqla Kilidaçmanı yenidən ayarlayın"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> yaxşı işləmirdi və silindi"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> və <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> yaxşı işləmirdi və silindi"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> yaxşı işləmirdi və silindi. Telefonu barmaq izi ilə kiliddən çıxarmaq üçün onu yenidən ayarlayın."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> və <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> yaxşı işləmirdi və silindi. Telefonu barmaq izi ilə kiliddən çıxarmaq üçün onları yenidən ayarlayın."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Üzlə Kilidaçmanı yenidən ayarlayın"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index db9a93f..e38706e 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -202,6 +202,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Poslovni profil više nije dostupan na ovom uređaju"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Previše pokušaja unosa lozinke"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administrator je ustupio uređaj za ličnu upotrebu"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Privatan prostor je uklonjen"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Organizacija ne dozvoljava privatne prostore na ovom upravljanom uređaju."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Uređajem se upravlja"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Organizacija upravlja ovim uređajem i može da nadgleda mrežni saobraćaj. Dodirnite za detalje."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikacije mogu da pristupaju vašoj lokaciji"</string>
@@ -284,8 +286,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Glasovna pomoć"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Zaključavanje"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Odgovori"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Novo obaveštenje"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fizička tastatura"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Bezbednost"</string>
@@ -2196,7 +2197,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"nalevo na D-pad-u"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"nadesno na D-pad-u"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"centar na D-pad-u"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Traka sa naslovima aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
@@ -2417,10 +2417,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Princip rada"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Na čekanju..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ponovo podesite otključavanje otiskom prsta"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> nije funkcionisao i izbrisali smo ga"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nisu funkcionisali i izbrisali smo ih"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> nije funkcionisao i izbrisali smo ga. Ponovo ga podesite da biste telefon otključavali otiskom prsta."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nisu funkcionisali i izbrisali smo ih. Ponovo ih podesite da biste telefon otključavali otiskom prsta."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Ponovo podesite otključavanje licem"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index ec19c2d..25ae5a7 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -203,6 +203,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Ваш працоўны профіль больш не даступны на гэтай прыладзе"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Занадта шмат спроб уводу пароля"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Адміністратар пераналадзіў прыладу для асабістага выкарыстання"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Прыватная прастора выдалена"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Ваша арганізацыя не дазваляе прыватныя прасторы на гэтай прыладзе пад яе кіраваннем."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Прылада знаходзіцца пад кіраваннем"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Ваша арганізацыя кіруе гэтай прыладай і можа сачыць за сеткавым трафікам. Дакраніцеся для атрымання дадатковай інфармацыі."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Праграмы могуць атрымліваць даныя пра ваша месцазнаходжанне"</string>
@@ -285,8 +287,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Галас. дапамога"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Блакіроўка"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Адказаць"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Новае апавяшчэнне"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Фізічная клавіятура"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Бяспека"</string>
@@ -2197,7 +2198,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Улева на панэлі кіравання"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Управа на панэлі кіравання"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"У цэнтр на панэлі кіравання"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Панэль субцітраў праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"."</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>
@@ -2418,10 +2418,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Як гэта працуе"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"У чаканні..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Наладзіць разблакіроўку адбіткам пальца паўторна"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"Адбітак пальца \"<xliff:g id="FINGERPRINT">%s</xliff:g>\" не працаваў належным чынам і быў выдалены"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"Адбіткі пальцаў \"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>\" і \"<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>\" не працавалі належным чынам і былі выдалены"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Адбітак пальца \"<xliff:g id="FINGERPRINT">%s</xliff:g>\" не працаваў належным чынам і быў выдалены. Каб мець магчымасць разблакіраваць тэлефон з дапамогай адбітка пальца, наладзьце яго яшчэ раз."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Адбіткі пальцаў \"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>\" і \"<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>\" не працавалі належным чынам і былі выдалены. Каб мець магчымасць разблакіраваць тэлефон з дапамогай адбітка пальца, наладзьце іх яшчэ раз."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Паўторна наладзьце распазнаванне твару"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 1b73710..6b388d8 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Служебният ви потребителски профил вече не е налице на това устройство"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Опитите за паролата са твърде много"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Администраторът предостави устройствотото за лична употреба"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Частното пространство бе премахнато"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Организацията ви не допуска частни пространства на това управлявано устройство."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Устройството се управлява"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Организацията ви управлява това устройство и може да наблюдава мрежовия трафик. Докоснете за подробности."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Прилож. имат достъп до местоположението ви"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Гласова помощ"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Заключване"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Отговор"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Ново известие"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Физическа клавиатура"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Сигурност"</string>
@@ -2195,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Контролен пад – ляво"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Контролен пад – дясно"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Контролен пад – център"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Лента за надписи на <xliff:g id="APP_NAME">%1$s</xliff:g>."</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>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Начин на работа"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Изчаква..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Повторно настройване на „Отключване с отпечатък“"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"Отпечатъкът „<xliff:g id="FINGERPRINT">%s</xliff:g>“ бе изтрит, защото не работеше добре"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"Отпечатъците „<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>“ и „<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>“ бяха изтрити, защото не работеха добре"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Отпечатъкът „<xliff:g id="FINGERPRINT">%s</xliff:g>“ бе изтрит, защото не работеше добре. Настройте го отново, за да отключвате телефона си с отпечатък."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Отпечатъците „<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>“ и „<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>“ бяха изтрити, защото не работеха добре. Настройте ги отново, за да отключвате телефона си с отпечатък."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Повторно настройване на „Отключване с лице“"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 0e32bbe6..3f338cc 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"আপনার কর্মস্থলের প্রোফাইলটি আর এই ডিভাইসে নেই"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"বহুবার ভুল পাসওয়ার্ড দিয়েছেন"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"ব্যক্তিগত কাজের জন্য অ্যাডমিন এই ডিভাইস ব্যবহার করার অনুমতি দেয়নি"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"প্রাইভেট স্পেস সরিয়ে দেওয়া হয়েছে"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"এই ম্যানেজ করা ডিভাইসে আপনার সংস্থা প্রাইভেট স্পেসের অনুমতি দেয় না।"</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"ডিভাইসটি পরিচালনা করা হচ্ছে"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"আপনার প্রতিষ্ঠান এই ডিভাইসটি পরিচালনা করে এবং এটির নেটওয়ার্ক ট্রাফিকের উপরে নজর রাখতে পারে। বিশদ বিবরণের জন্য ট্যাপ করুন।,"</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"অ্যাপগুলি আপনার লোকেশন অ্যাক্সেস করতে পারবে"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"ভয়েস সহায়তা"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"লকডাউন"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"৯৯৯+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"উত্তর দিন"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"নতুন বিজ্ঞপ্তি"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"ফিজিক্যাল কীবোর্ড"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"নিরাপত্তা"</string>
@@ -2195,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"ডিপ্যাড (Dpad)-এর বাঁদিকে"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"ডিপ্যাড (Dpad)-এর ডানদিকে"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"ডিপ্যাড (Dpad)-এর মাঝখানে"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g>-এর ক্যাপশন বার।"</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>
@@ -2396,7 +2396,7 @@
     <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"কীবোর্ড লেআউট <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>-এ সেট করা আছে… পালটাতে ট্যাপ করুন।"</string>
     <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"ফিজিক্যাল কীবোর্ড কনফিগার করা হয়েছে"</string>
     <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"কীবোর্ড দেখতে ট্যাপ করুন"</string>
-    <string name="profile_label_private" msgid="6463418670715290696">"ব্যক্তিগত"</string>
+    <string name="profile_label_private" msgid="6463418670715290696">"প্রাইভেট"</string>
     <string name="profile_label_clone" msgid="769106052210954285">"ক্লোন করুন"</string>
     <string name="profile_label_work" msgid="3495359133038584618">"অফিস"</string>
     <string name="profile_label_work_2" msgid="4691533661598632135">"২য় অফিস"</string>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"এটি কীভাবে কাজ করে"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"বাকি আছে…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"\'ফিঙ্গারপ্রিন্ট আনলক\' আবার সেট-আপ করুন"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> ভালোভাবে কাজ করছিল না এবং সেটি মুছে ফেলা হয়েছে"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ও <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ভালোভাবে কাজ করছিল না এবং মুছে ফেলা হয়েছে"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ভালোভাবে কাজ করছিল না বলে সেটি মুছে ফেলা হয়েছে। ফিঙ্গারপ্রিন্ট ব্যবহার করে আপনার ফোন আনলক করতে হলে এটি আবার সেট-আপ করুন।"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ও <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ভালোভাবে কাজ করছিল না বলে মুছে ফেলা হয়েছে। ফিঙ্গারপ্রিন্ট ব্যবহার করে আপনার ফোন আনলক করতে হলে সেগুলি আবার সেট-আপ করুন।"</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"\'ফেস আনলক\' আবার সেট-আপ করুন"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 63a83f0..0438302 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -202,6 +202,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Radni profil više nije dostupan na ovom uređaju"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Previše puta ste pokušali otključati uređaj"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administrator je ustupio uređaj za ličnu upotrebu"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Privatni prostor je uklonjen"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Organizacija ne dozvoljava privatne prostore na ovom uređaju kojim se upravlja."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Uređajem se upravlja."</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Vaša organizacija upravlja ovim uređajem i može pratiti mrežni saobraćaj. Dodirnite za detalje."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikacije mogu pristupiti vašoj lokaciji"</string>
@@ -2195,7 +2197,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Upravljač lijevo"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Upravljač desno"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Upravljač sredina"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Traka za natpis aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
@@ -2416,10 +2417,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kako ovo funkcionira"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Na čekanju…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ponovo postavite otključavanje otiskom prsta"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"Otisak prsta <xliff:g id="FINGERPRINT">%s</xliff:g> nije dobro funkcionirao, pa je izbrisan"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"Otisci prstiju <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nisu dobro funkcionirali, pa su izbrisani"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Otisak prsta <xliff:g id="FINGERPRINT">%s</xliff:g> nije dobro funkcionirao, pa je izbrisan. Postavite ga ponovo da otključavate telefon otiskom prsta."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Otisci prstiju <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nisu dobro funkcionirali, pa su izbrisani. Postavite ih ponovo da otključavate telefon otiskom prsta."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Ponovo postavite otključavanje licem"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 1fd814d..21f397b 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -202,6 +202,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"El teu perfil de treball ja no està disponible en aquest dispositiu"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Has intentat introduir la contrasenya massa vegades"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"L\'administrador ha cedit el dispositiu per a ús personal"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"S\'ha suprimit l\'espai privat"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"La teva organització no permet espais privats en aquest dispositiu gestionat."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"El dispositiu està gestionat"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"La teva organització gestiona aquest dispositiu i és possible que supervisi el trànsit de xarxa. Toca per obtenir més informació."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Les aplicacions poden accedir a la teva ubicació"</string>
@@ -284,8 +286,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Assist. per veu"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Bloqueig de seguretat"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"+999"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Respon"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Notificació nova"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Teclat físic"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Seguretat"</string>
@@ -2196,7 +2197,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Creu direccional: esquerra"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Creu direccional: dreta"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Creu direccional: centre"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Barra de títol de l\'aplicació <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
@@ -2417,10 +2417,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Com funciona"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendent..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Torna a configurar Desbloqueig amb empremta digital"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> no funcionava correctament i s\'ha suprimit"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> no funcionaven correctament i s\'han suprimit"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> no funcionava correctament i s\'ha suprimit. Torna a configurar-la per desbloquejar el telèfon amb l\'empremta digital."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> no funcionaven correctament i s\'han suprimit. Torna a configurar-les per desbloquejar el telèfon amb l\'empremta digital."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Torna a configurar Desbloqueig facial"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index be4128f..4013a03 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -203,6 +203,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Váš pracovní profil v tomto zařízení již není k dispozici"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Příliš mnoho pokusů o zadání hesla"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administrátor zařízení uvolnil k osobnímu používání"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Soukromý prostor byl odstraněn"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Vaše organizace na tomto spravovaném zařízení soukromé prostory nepovoluje."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Zařízení je spravováno"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Toto zařízení je spravováno vaší organizací, která může sledovat síťový provoz. Podrobnosti zobrazíte klepnutím."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikace mají přístup k vaší poloze"</string>
@@ -1636,7 +1638,7 @@
     <string name="issued_by" msgid="7872459822431585684">"Vydal:"</string>
     <string name="validity_period" msgid="1717724283033175968">"Platnost:"</string>
     <string name="issued_on" msgid="5855489688152497307">"Datum vydání:"</string>
-    <string name="expires_on" msgid="1623640879705103121">"Platnost vyprší:"</string>
+    <string name="expires_on" msgid="1623640879705103121">"Platnost skončí:"</string>
     <string name="serial_number" msgid="3479576915806623429">"Sériové číslo:"</string>
     <string name="fingerprints" msgid="148690767172613723">"Digitální otisky:"</string>
     <string name="sha256_fingerprint" msgid="7103976380961964600">"Digitální otisk SHA-256"</string>
@@ -2196,7 +2198,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad doleva"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad doprava"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad střed"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Popisek aplikace <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
@@ -2397,9 +2398,9 @@
     <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Rozložení klávesnice je nastaveno na <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Klepnutím jej změníte."</string>
     <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Fyzické klávesnice byly nakonfigurovány"</string>
     <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Klepnutím zobrazíte klávesnice"</string>
-    <string name="profile_label_private" msgid="6463418670715290696">"Soukromé"</string>
+    <string name="profile_label_private" msgid="6463418670715290696">"Soukromý"</string>
     <string name="profile_label_clone" msgid="769106052210954285">"Klonovat"</string>
-    <string name="profile_label_work" msgid="3495359133038584618">"Práce"</string>
+    <string name="profile_label_work" msgid="3495359133038584618">"Pracovní"</string>
     <string name="profile_label_work_2" msgid="4691533661598632135">"Práce 2"</string>
     <string name="profile_label_work_3" msgid="4834572253956798917">"Práce 3"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Test"</string>
@@ -2417,10 +2418,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Jak to funguje"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Čeká na vyřízení…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Opětovné nastavení odemknutí otiskem prstu"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> nefungoval správně a byl vymazán"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> a <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nefungovaly správně a byly vymazány"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> nefungoval správně a byl vymazán. Pokud chcete telefon odemykat otiskem prstu, nastavte jej znovu."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> a <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nefungovaly správně a byly vymazány. Pokud chcete telefon odemykat otiskem prstu, nastavte je znovu."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Nastavte odemknutí obličejem znovu"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 9b2844b..5226d56 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Din arbejdsprofil er ikke længere tilgængelig på denne enhed"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"For mange mislykkede adgangskodeforsøg"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administratoren har gjort personlig brug af enheden utilgængelig"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Det private område er fjernet"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Din organisation tillader ikke private områder på denne administrerede enhed."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Dette er en administreret enhed"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Din organisation administrerer denne enhed og kan overvåge netværkstrafik. Tryk for at se info."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Apps kan få adgang til din lokation"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"D-pad, venstre"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"D-pad, højre"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"D-pad, midten"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Titellinje for <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Sådan fungerer det"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Afventer…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Konfigurer fingeroplåsning igen"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> virkede ikke optimalt og er derfor slettet"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> og <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> virkede ikke optimalt og er derfor slettet"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> virkede ikke optimalt og er derfor slettet. Konfigurer den igen for at bruge fingeroplåsning på din telefon."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> og <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> virkede ikke optimalt og er derfor slettet. Konfigurer dem igen for at bruge dit fingeraftryk til at låse din telefon op."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Konfigurer ansigtsoplåsning igen"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 1df8954..949150a 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Dein Arbeitsprofil ist auf diesem Gerät nicht mehr verfügbar"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Zu viele falsche Passworteingaben"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administrator hat das Gerät zur persönlichen Nutzung abgegeben"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Vertrauliches Profil entfernt"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Deine Organisation erlaubt auf diesem verwalteten Gerät keine vertraulichen Profile."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Dies ist ein verwaltetes Gerät"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Deine Organisation verwaltet dieses Gerät und überprüft unter Umständen den Netzwerkverkehr. Tippe hier, um weitere Informationen zu erhalten."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Apps können auf deinen Standort zugreifen"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Sprachassistent"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Sperren"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Antworten"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Neue Benachrichtigung"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Physische Tastatur"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Sicherheit"</string>
@@ -2195,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Steuerkreuz nach links"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Steuerkreuz nach rechts"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Steuerkreuz Mitte"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Untertitelleiste von <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"So funktionierts"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Ausstehend…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Entsperrung per Fingerabdruck neu einrichten"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> hat nicht einwandfrei funktioniert und wurde gelöscht"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> und <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> haben nicht einwandfrei funktioniert und wurden gelöscht"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> hat nicht einwandfrei funktioniert und wurde gelöscht. Richte ihn noch einmal ein, um dein Smartphone per Fingerabdruck zu entsperren."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> und <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> haben nicht einwandfrei funktioniert und wurden gelöscht. Richte sie noch einmal ein, um dein Smartphone per Fingerabdruck zu entsperren."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Entsperrung per Gesichtserkennung neu einrichten"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 3c7d167..11dac6c 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Το προφίλ εργασίας σας δεν είναι πια διαθέσιμο σε αυτήν τη συσκευή"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Πάρα πολλές προσπάθειες εισαγωγής κωδικού πρόσβασης"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Συσκευή από την οποία αποσύρθηκε ο διαχειριστής για προσωπική χρήση"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Ο ιδιωτικός χώρος καταργήθηκε"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Ο οργανισμός σας δεν επιτρέπει ιδιωτικούς χώρους σε αυτή τη διαχειριζόμενη συσκευή."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Η συσκευή είναι διαχειριζόμενη"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Ο οργανισμός σας διαχειρίζεται αυτήν τη συσκευή και ενδέχεται να παρακολουθεί την επισκεψιμότητα δικτύου. Πατήστε για λεπτομέρειες."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Οι εφαρμογές μπορούν να αποκτήσουν πρόσβαση στην τοποθεσία σας"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Φων.υποβοηθ."</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Κλείδωμα"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Απάντηση"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Νέα ειδοποίηση"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Κανονικό πληκτρολόγιο"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Ασφάλεια"</string>
@@ -2195,14 +2196,13 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad αριστερά"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad δεξιά"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad κέντρο"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Γραμμή υποτίτλων για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>."</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>
     <string name="conversation_title_fallback_one_to_one" msgid="1980753619726908614">"Συνομιλία"</string>
     <string name="conversation_title_fallback_group_chat" msgid="456073374993104303">"Ομαδική συνομιλία"</string>
     <string name="unread_convo_overflow" msgid="920517615597353833">"<xliff:g id="MAX_UNREAD_COUNT">%1$d</xliff:g>+"</string>
-    <string name="resolver_personal_tab" msgid="2051260504014442073">"Προσωπικό"</string>
+    <string name="resolver_personal_tab" msgid="2051260504014442073">"Προσωπικός"</string>
     <string name="resolver_work_tab" msgid="2690019516263167035">"Εργασία"</string>
     <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"Προσωπική προβολή"</string>
     <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"Προβολή εργασίας"</string>
@@ -2396,7 +2396,7 @@
     <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Η διάταξη πληκτρολογίου ορίστηκε σε <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Πατήστε για αλλαγή."</string>
     <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Τα φυσικά πληκτρολόγια διαμορφώθηκαν"</string>
     <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Πατήστε για να δείτε πληκτρολόγια"</string>
-    <string name="profile_label_private" msgid="6463418670715290696">"Ιδιωτικό"</string>
+    <string name="profile_label_private" msgid="6463418670715290696">"Ιδιωτικός"</string>
     <string name="profile_label_clone" msgid="769106052210954285">"Κλώνος"</string>
     <string name="profile_label_work" msgid="3495359133038584618">"Εργασία"</string>
     <string name="profile_label_work_2" msgid="4691533661598632135">"Εργασία 2"</string>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Πώς λειτουργεί"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Σε εκκρεμότητα…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Επαναρρύθμιση λειτουργίας Ξεκλείδωμα με δακτυλικό αποτύπωμα"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"Το δακτυλικό αποτύπωμα <xliff:g id="FINGERPRINT">%s</xliff:g> δεν λειτουργούσε καλά και διαγράφηκε"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"Τα δακτυλικά αποτυπώματα <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> και <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> δεν λειτουργούσαν καλά και διαγράφηκαν"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Το δακτυλικό αποτύπωμα <xliff:g id="FINGERPRINT">%s</xliff:g> δεν λειτουργούσε καλά και διαγράφηκε. Ρυθμίστε το ξανά για να ξεκλειδώνετε το τηλέφωνο με το δακτυλικό αποτύπωμά σας."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Τα δακτυλικά αποτυπώματα <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> και <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> δεν λειτουργούσαν καλά και διαγράφηκαν. Ρυθμίστε τα ξανά για να ξεκλειδώνετε το τηλέφωνο με το δακτυλικό αποτύπωμά σας."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Επαναρρύθμιση λειτουργίας Ξεκλείδωμα με το πρόσωπο"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 0c782a7..1effe7c 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Your work profile is no longer available on this device"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Too many password attempts"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Admin relinquished device for personal use"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Private space removed"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Your organisation does not allow private spaces on this managed device."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Device is managed"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Your organisation manages this device and may monitor network traffic. Tap for details."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Apps can access your location"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad left"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad right"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad centre"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Caption bar of <xliff:g id="APP_NAME">%1$s</xliff:g>."</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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Set up Fingerprint Unlock again"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> wasn\'t working well and was deleted"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> weren\'t working well and were deleted"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> wasn\'t working well and was deleted. Set it up again to unlock your phone with your fingerprint."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> weren\'t working well and were deleted. Set them up again to unlock your phone with your fingerprint."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Set up Face Unlock again"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index d1894b5..4abc581 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Your work profile is no longer available on this device"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Too many password attempts"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Admin relinquished device for personal use"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Private space removed"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Your organisation does not allow private spaces on this managed device."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Device is managed"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Your organization manages this device and may monitor network traffic. Tap for details."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Apps can access your location"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad Left"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad Right"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad Center"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Caption bar of <xliff:g id="APP_NAME">%1$s</xliff:g>."</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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Set up Fingerprint Unlock again"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> wasn\'t working well and was deleted"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> weren\'t working well and were deleted"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> wasn\'t working well and was deleted. Set it up again to unlock your phone with fingerprint."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> weren\'t working well and were deleted. Set them up again to unlock your phone with your fingerprint."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Set up Face Unlock again"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 632585f..425d88c 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Your work profile is no longer available on this device"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Too many password attempts"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Admin relinquished device for personal use"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Private space removed"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Your organisation does not allow private spaces on this managed device."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Device is managed"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Your organisation manages this device and may monitor network traffic. Tap for details."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Apps can access your location"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad left"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad right"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad centre"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Caption bar of <xliff:g id="APP_NAME">%1$s</xliff:g>."</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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Set up Fingerprint Unlock again"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> wasn\'t working well and was deleted"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> weren\'t working well and were deleted"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> wasn\'t working well and was deleted. Set it up again to unlock your phone with your fingerprint."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> weren\'t working well and were deleted. Set them up again to unlock your phone with your fingerprint."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Set up Face Unlock again"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 1c55884..56ff14a 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Your work profile is no longer available on this device"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Too many password attempts"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Admin relinquished device for personal use"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Private space removed"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Your organisation does not allow private spaces on this managed device."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Device is managed"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Your organisation manages this device and may monitor network traffic. Tap for details."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Apps can access your location"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad left"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad right"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad centre"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Caption bar of <xliff:g id="APP_NAME">%1$s</xliff:g>."</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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"How it works"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pending…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Set up Fingerprint Unlock again"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> wasn\'t working well and was deleted"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> weren\'t working well and were deleted"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> wasn\'t working well and was deleted. Set it up again to unlock your phone with your fingerprint."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> and <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> weren\'t working well and were deleted. Set them up again to unlock your phone with your fingerprint."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Set up Face Unlock again"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index e08f934..e599c03 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎‎‎‏‏‏‏‎‎‎‏‎‎‏‎‏‏‎‎‏‎‎‏‏‎‏‎‎‏‏‎‎‎‎‎‏‎‎‎‏‏‏‏‎‏‎‎‎‎‎‎‎Your work profile is no longer available on this device‎‏‎‎‏‎"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‎‎‎‏‏‎‎‎‎‎‏‎‎‏‏‏‎‎‎‎‎‏‎‎‎‎‏‏‏‏‏‏‏‎‎‏‏‏‏‏‏‎Too many password attempts‎‏‎‎‏‎"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‏‎‏‎‎‎‏‎‎‎‏‏‏‎‎‏‎‎‎‏‏‎‎‏‎‎‎‏‎‎‎‏‏‎‎‏‎‏‏‏‎‏‏‏‏‎‎‎‎‏‎‎‏‏‎‎‎Admin relinquished device for personal use‎‏‎‎‏‎"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‏‎‎‏‏‎‎‏‏‎‎‎‎‏‏‎‏‎‏‎‎‎‏‏‎‏‏‏‏‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‎‏‏‎Private space removed‎‏‎‎‏‎"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‎‎‎‎‎‎‏‎‏‎‎‏‎‎‎‏‎‏‏‎‏‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‎‏‏‎‎‏‎‎‎‎‏‎‎‎‎‎‏‎Your organisation does not allow private spaces on this managed device.‎‏‎‎‏‎"</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‏‎‏‏‎‎‏‏‏‎‏‏‎‎‏‎‎‏‎‎‎‏‎‎‏‏‎‎‎‏‏‎‎‏‏‏‎‏‎‏‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‎Device is managed‎‏‎‎‏‎"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‎‏‎‎‏‏‎‏‎‏‏‏‏‎‎‎‏‏‎‏‏‎‎‎‏‏‎‏‎‎‎‎‏‎‏‎‏‎‎‎‏‏‎‏‎‎‏‎‏‎‎‎‏‎‏‎‎‎‎‎Your organization manages this device and may monitor network traffic. Tap for details.‎‏‎‎‏‎"</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‎‎‏‏‏‏‎‏‎‏‏‎‎‎‏‏‎‎‏‏‎‎‏‎‎‎‏‏‏‎‎‎‎‎‎‏‏‏‎‏‎‎‏‏‎‎‏‏‏‎‏‏‎‏‎‎‎Apps can access your location‎‏‎‎‏‎"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‎‎‏‎‏‏‏‎‎‎‏‎‏‎‏‏‏‎‏‏‎‎‏‎‎‎‏‏‎‎‏‎‏‎‎‏‎‎‎‎‎‏‎‏‎‏‏‎‎‎‎‎Dpad Left‎‏‎‎‏‎"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‎‎‏‏‏‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎‎‏‎‏‎‏‎‏‏‎‏‏‎‎‏‎‏‏‎‎‎‏‏‎‎‎‏‎Dpad Right‎‏‎‎‏‎"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‏‏‎‎‏‏‏‏‎‎‎‎‎‎‎‎‏‏‏‎‏‏‏‎‏‎‎‎‎‎‏‎‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‏‏‏‏‎‏‎Dpad Center‎‏‎‎‏‎"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‏‎‎‎‎‏‎‏‏‏‎‏‏‎‏‎‏‏‏‎‎‎‎‎‏‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‎‏‎‏‏‏‎‏‎‎‏‎‏‎‎Caption bar of ‎‏‎‎‏‏‎<xliff:g id="APP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎.‎‏‎‎‏‎"</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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‏‎‏‏‏‎‏‎‏‏‎‎‎‎‏‏‏‎‏‎‎‎‎‏‏‎‏‎‎‎‏‎‏‏‏‏‎‎‏‏‎‎‎‎‏‏‎‎‎‎‏‏‏‎How it works‎‏‎‎‏‎"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‎‏‎‎‎‏‎‎‎‏‎‎‏‎‎‏‏‏‏‎‏‎‏‎‏‎‏‏‏‏‏‎‏‏‏‎‏‎‏‎‏‏‎‏‎‏‏‏‎‏‎‏‎‏‏‎‏‎Pending...‎‏‎‎‏‎"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‏‎‏‎‏‏‎‏‎‏‏‎‎‎‎‏‏‎‎‎‏‎‎‏‏‎‏‎‏‎‏‏‎‎‎‏‏‎‎‏‏‏‏‏‎‎‏‏‏‏‎‏‎‏‎Set up Fingerprint Unlock again‎‏‎‎‏‎"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‎‏‏‎‎‏‎‏‏‏‏‎‏‏‎‎‎‎‏‏‎‎‏‎‏‎‎‏‎‏‎‏‎‎‎‏‎‏‎‎‏‏‎‏‎‏‎‎‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="FINGERPRINT">%s</xliff:g>‎‏‎‎‏‏‏‎ wasn\'t working well and was deleted‎‏‎‎‏‎"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‎‏‎‏‏‎‏‎‏‏‎‏‎‎‏‎‏‎‎‎‏‏‎‎‏‎‎‎‏‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‏‏‎‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>‎‏‎‎‏‏‏‎ and ‎‏‎‎‏‏‎<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>‎‏‎‎‏‏‏‎ weren\'t working well and were deleted‎‏‎‎‏‎"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‎‎‏‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‎‎‎‎‏‏‎‎‎‏‎‎‎‏‎‎‎‏‎‏‎‏‎‏‏‎‎‎‎‎‎‎‏‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="FINGERPRINT">%s</xliff:g>‎‏‎‎‏‏‏‎ wasn\'t working well and was deleted. Set it up again to unlock your phone with fingerprint.‎‏‎‎‏‎"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‏‎‏‎‎‏‏‎‎‎‏‎‎‏‏‏‎‏‎‎‏‎‎‎‏‎‎‏‎‏‎‏‏‏‏‎‏‎‎‎‏‎‎‎‎‎‎‏‎‎‏‏‎<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>‎‏‎‎‏‏‏‎ and ‎‏‎‎‏‏‎<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>‎‏‎‎‏‏‏‎ weren\'t working well and were deleted. Set them up again to unlock your phone with your fingerprint.‎‏‎‎‏‎"</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‏‎‎‏‎‎‏‏‏‎‏‏‏‎‎‏‎‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‏‎‎‏‎‎‎‎‎‎‏‎‏‏‎‏‏‏‎‎‎‏‎Set up Face Unlock again‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 30a0457..f5859bf 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -202,6 +202,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Tu perfil de trabajo ya no está disponible en este dispositivo"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Demasiados intentos para ingresar la contraseña"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"El administrador no permite hacer un uso personal del dispositivo"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Se quitó el espacio privado"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Tu organización no permite espacios privados en este dispositivo administrado."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Dispositivo administrado"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Tu organización administra este dispositivo y es posible que controle el tráfico de red. Presiona para obtener más información."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Las apps pueden acceder a tu ubicación"</string>
@@ -284,8 +286,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Asistente voz"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Bloqueo"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Responder"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Notificación nueva"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Teclado físico"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Seguridad"</string>
@@ -1190,8 +1191,8 @@
     <string name="deleteText" msgid="4200807474529938112">"Eliminar"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Método de entrada"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Acciones de texto"</string>
-    <string name="error_handwriting_unsupported" msgid="7809438534946014050">"La función Escritura a mano no es compatible en este campo"</string>
-    <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"La función Escritura a mano no es compatible en los campos de contraseñas"</string>
+    <string name="error_handwriting_unsupported" msgid="7809438534946014050">"La función Escritura a mano no está disponible en este campo"</string>
+    <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"La Escritura a mano no está disponible en campos de contraseñas"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Atrás"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Cambiar método de entrada"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Queda poco espacio de almacenamiento"</string>
@@ -2196,7 +2197,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Pad direccional: izquierda"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Pad direccional: derecha"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Pad direccional: centro"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Barra de subtítulos de <xliff:g id="APP_NAME">%1$s</xliff:g>."</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>
@@ -2417,10 +2417,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cómo funciona"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendiente…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Vuelve a configurar el Desbloqueo con huellas dactilares"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"Se borró <xliff:g id="FINGERPRINT">%s</xliff:g> porque no funcionaba correctamente"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"Se borraron <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> y <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> porque no funcionaban correctamente"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Se borró <xliff:g id="FINGERPRINT">%s</xliff:g> porque no funcionaba correctamente. Vuelve a configurarla para desbloquear el teléfono con la huella dactilar."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Se borraron <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> y <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> porque no funcionaban correctamente. Vuelve a configurarlas para desbloquear el teléfono con la huella dactilar."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Vuelve a configurar el Desbloqueo facial"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 5079fdf..d8b1f59 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -202,6 +202,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Tu perfil de trabajo ya no está disponible en este dispositivo"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Has fallado demasiadas veces al introducir la contraseña"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"El administrador no permite hacer un uso personal del dispositivo"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Espacio privado eliminado"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Tu organización no permite espacios privados en este dispositivo gestionado."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"El dispositivo está administrado"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Tu organización administra este dispositivo y puede supervisar el tráfico de red. Toca la notificación para obtener más información."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Las aplicaciones pueden acceder a tu ubicación"</string>
@@ -284,8 +286,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Asistente voz"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Bloqueo de seguridad"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"&gt; 999"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Responder"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Notificación nueva"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Teclado físico"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Seguridad"</string>
@@ -1190,8 +1191,8 @@
     <string name="deleteText" msgid="4200807474529938112">"Eliminar"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Método de introducción de texto"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Acciones de texto"</string>
-    <string name="error_handwriting_unsupported" msgid="7809438534946014050">"Escritura a mano no está disponible en este campo"</string>
-    <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Escritura a mano no está disponible en campos de contraseña"</string>
+    <string name="error_handwriting_unsupported" msgid="7809438534946014050">"La escritura a mano no está disponible en este campo"</string>
+    <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"La escritura a mano no está disponible en campos de contraseña"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Atrás"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Cambiar método de introducción de texto"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Queda poco espacio"</string>
@@ -2196,7 +2197,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Cruceta: izquierda"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Cruceta: derecha"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Cruceta: centro"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Barra de subtítulos de <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
@@ -2417,10 +2417,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cómo funciona"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendiente..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configura Desbloqueo con huella digital de nuevo"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> no funcionaba correctamente y se ha eliminado"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> y <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> no funcionaban correctamente y se han eliminado"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> no funcionaba correctamente y se ha eliminado. Configúrala de nuevo para desbloquear el teléfono con la huella digital."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> y <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> no funcionaban correctamente y se han eliminado. Configúralas de nuevo para desbloquear el teléfono con la huella digital."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Configura Desbloqueo facial de nuevo"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 63e1756..77e665a 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Teie tööprofiil pole selles seadmes enam saadaval"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Liiga palju paroolikatseid"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administraator keelas seadme isikliku kasutamise"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Privaatne ruum on eemaldatud"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Teie organisatsioon ei luba selles hallatud seadmes kasutada privaatseid ruume."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Seade on hallatud"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Teie organisatsioon haldab seda seadet ja võib jälgida võrguliiklust. Puudutage üksikasjade vaatamiseks."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Rakendused pääsevad teie asukohale juurde"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Suunaklahvistiku vasaknool"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Suunaklahvistiku paremnool"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Suunaklahvistiku keskmine nupp"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> pealkirjariba."</string>
     <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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Tööpõhimõtted"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Ootel …"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Seadistage sõrmejäljega avamine uuesti"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> ei töötanud hästi ja kustutati"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ja <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ei töötanud hästi ning kustutati"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ei töötanud hästi ja kustutati. Telefoni sõrmejäljega avamiseks seadistage see uuesti."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ja <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ei töötanud hästi ning kustutati. Telefoni sõrmejäljega avamiseks seadistage need uuesti."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Seadistage näoga avamine uuesti"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 4fb4726..9bf2a4d 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Laneko profila ez dago erabilgarri gailu honetan"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Gehiegitan saiatu zara pasahitza idazten"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Erabilera pertsonalerako utzi du gailua administratzaileak"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Kendu egin da eremu pribatua"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Zure erakundeak ez ditu onartzen eremu pribatuak kudeatutako gailu honetan."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Jabeak kudeatzen du gailua"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Erakundeak kudeatzen du gailua eta baliteke sareko trafikoa gainbegiratzea. Sakatu hau xehetasunak ikusteko."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikazioek zure kokapena atzi dezakete"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Ahots-laguntza"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Blokeatu"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Erantzun"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Jakinarazpen berria"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Teklatu fisikoa"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Segurtasuna"</string>
@@ -2195,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Norabide-kontrolagailuko ezkerreko botoia"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Norabide-kontrolagailuko eskuineko botoia"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Norabide-kontrolagailuko erdiko botoia"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioko azpitituluen barra."</string>
     <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>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Nola funtzionatzen du?"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Zain…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Konfiguratu berriro hatz-marka bidez desblokeatzeko eginbidea"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> ezabatu egin da, ez zuelako ondo funtzionatzen"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> eta <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ezabatu egin dira, ez zutelako ondo funtzionatzen"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ezabatu egin da, ez zuelako ondo funtzionatzen. Telefonoa hatz-markarekin desblokeatzeko, konfigura ezazu berriro."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> eta <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ezabatu egin dira, ez zutelako ondo funtzionatzen. Telefonoa hatz-markarekin desblokeatzeko, konfigura itzazu berriro."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Konfiguratu berriro aurpegi bidez desblokeatzeko eginbidea"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 9a57d80..308260f 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"نمایه کاری شما دیگر در این دستگاه دردسترس نیست"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"تلاش‌های بسیار زیادی برای وارد کردن گذرواژه انجام شده است"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"سرپرست از این دستگاه برای استفاده شخصی چشم‌پوشی کرد"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"فضای خصوصی حذف شد"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"سازمان شما اجازه نمی‌دهد در این دستگاه مدیریت‌شده فضای خصوصی وجود داشته باشد."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"دستگاه مدیریت می‌شود"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"سازمانتان این دستگاه را مدیریت می‌کند و ممکن است ترافیک شبکه را پایش کند. برای اطلاع از جزئیات، ضربه بزنید."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"برنامه‌ها می‌توانند به مکانتان دسترسی پیدا کنند"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"دستیار صوتی"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"قفل همه"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"۹۹۹+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"پاسخ دادن"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"اعلان جدید"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"صفحه‌کلید فیزیکی"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"امنیت"</string>
@@ -607,8 +608,8 @@
     <string name="permdesc_bluetooth_connect" product="default" msgid="4546016548795544617">"به برنامه اجازه می‌دهد به دستگاه‌های بلوتوث مرتبط‌شده متصل شود"</string>
     <string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"تبلیغ در دستگاه‌های بلوتوث اطراف"</string>
     <string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"برنامه مجاز می‌شود در دستگاه‌های بلوتوث اطراف تبلیغ کند."</string>
-    <string name="permlab_uwb_ranging" msgid="8141915781475770665">"مشخص کردن موقعیت نسبی بین دستگاه‌های باند فوق‌وسیع اطراف"</string>
-    <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"به برنامه اجازه داده می‌شود موقعیت نسبی بین دستگاه‌های باند فوق‌وسیع اطراف را مشخص کند"</string>
+    <string name="permlab_uwb_ranging" msgid="8141915781475770665">"مشخص کردن موقعیت نسبی بین دستگاه‌های «فراپهن‌باند» اطراف"</string>
+    <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"به برنامه اجازه داده می‌شود موقعیت نسبی بین دستگاه‌های «فراپهن‌باند» اطراف را مشخص کند"</string>
     <string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"‏برقراری تعامل با دستگاه‌های Wi-Fi اطراف"</string>
     <string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"‏به برنامه اجازه می‌دهد در دستگاه‌های Wi-Fi اطراف تبلیغ کند، به آن‌ها متصل شود، و موقعیت نسبی آن‌ها را تشخیص دهد"</string>
     <string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"‏اطلاعات ترجیحی سرویس پولی NFC"</string>
@@ -2195,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"پد کنترل چپ"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"پد کنترل راست"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"پد کنترل وسط"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"نوار شرح <xliff:g id="APP_NAME">%1$s</xliff:g>."</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>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"روش کار"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"درحال تعلیق…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"راه‌اندازی مجدد «قفل‌گشایی با اثر انگشت»"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"‫<xliff:g id="FINGERPRINT">%s</xliff:g> خوب کار نمی‌کرد و حذف شد"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"‫<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> و <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> خوب کار نمی‌کردند و حذف شدند"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> خوب کار نمی‌کرد و حذف شد. برای باز کردن قفل تلفن با اثر انگشت، آن را دوباره راه‌اندازی کنید."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"‫<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> و <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> خوب کار نمی‌کرد و حذف شد. برای باز کردن قفل تلفن با اثر انگشت، آن‌ها را دوباره راه‌اندازی کنید."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"راه‌اندازی مجدد «قفل‌گشایی با چهره»"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 7062a89..db579f2 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Työprofiilisi ei ole enää käytettävissä tällä laitteella."</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Liikaa salasanayrityksiä"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Järjestelmänvalvoja luovutti laitteen henkilökohtaiseen käyttöön"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Yksityinen tila poistettu"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Organisaatio ei salli yksityisiä tiloja tällä hallinnoidulla laitteella."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Hallinnoitu laite"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Organisaatiosi hallinnoi tätä laitetta ja voi tarkkailla verkkoliikennettä. Katso lisätietoja napauttamalla."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Sovelluksilla on pääsy sijaintiisi"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Ääniapuri"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Lukitse"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Vastaa"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Uusi ilmoitus"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fyysinen näppäimistö"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Turvallisuus"</string>
@@ -2195,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Suuntanäppäimistö: vasen painike"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Suuntanäppäimistö: oikea painike"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Suuntanäppäimistö: keskipainike"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Tekstityspalkki: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <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>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Näin se toimii"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Odottaa…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ota sormenjälkiavaus uudelleen käyttöön"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> ei toiminut kunnolla, ja se poistettiin"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ja <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> eivät toimineet kunnolla, ja ne poistettiin"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ei toiminut kunnolla, ja se poistettiin. Ota se uudelleen käyttöön, jotta voit avata puhelimen lukituksen sormenjäljellä."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ja <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> eivät toimineet kunnolla, ja ne poistettiin. Ota ne uudelleen käyttöön, jotta voit avata puhelimen lukituksen sormenjäljellä."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Ota kasvojentunnistusavaus uudelleen käyttöön"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 9aa982f..8888005 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -202,6 +202,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Votre profil professionnel n\'est plus accessible sur cet appareil"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Trop de tentatives d\'entrée du mot de passe"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"L\'administrateur a libéré l\'appareil pour un usage personnel"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Espace privé retiré"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Votre organisation n\'autorise pas les espaces privés sur cet appareil géré."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"L\'appareil est géré"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Votre organisation gère cet appareil et peut surveiller le trafic réseau. Touchez ici pour obtenir plus d\'information."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Les applications peuvent accéder à votre position"</string>
@@ -2195,7 +2197,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Pavé directionnel – gauche"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Pavé directionnel – droite"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Pavé directionnel – centre"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Barre de légende de l\'application <xliff:g id="APP_NAME">%1$s</xliff:g>."</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>
@@ -2416,10 +2417,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Fonctionnement"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"En attente…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configurer le Déverrouillage par empreinte digitale à nouveau"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> ne fonctionnait pas bien et a été supprimée"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> et <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ne fonctionnaient pas bien et ont été supprimées"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ne fonctionnait pas bien et a été supprimée. Configurez-le à nouveau pour déverrouiller votre téléphone avec l\'empreinte digitale."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> et <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ne fonctionnaient pas bien et ont été supprimées. Configurez-les à nouveau pour déverrouiller votre téléphone avec votre empreinte digitale."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Configurer le Déverrouillage par reconnaissance faciale à nouveau"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index bb1b30d..4c2835d 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -162,7 +162,7 @@
     <string name="scNullCipherIssueEncryptedTitle" msgid="234717016411824969">"Connecté au réseau chiffré <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueEncryptedSummary" msgid="8577510708842150475">"La connexion à la carte SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g> est désormais plus sécurisée"</string>
     <string name="scNullCipherIssueNonEncryptedTitle" msgid="3978071464929453915">"Connecté à un réseau non chiffré"</string>
-    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Les appels, les messages et les données sont actuellement plus vulnérables lorsque vous utilisez votre carte SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
+    <string name="scNullCipherIssueNonEncryptedSummaryNotification" msgid="7386936934128110388">"Appels, messages et données sont plus vulnérables si vous utilisez votre carte SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</string>
     <string name="scNullCipherIssueNonEncryptedSummary" msgid="5093428974513703253">"Les appels, les messages et les données sont actuellement plus vulnérables lorsque vous utilisez votre carte SIM <xliff:g id="NETWORK_NAME">%1$s</xliff:g>.\n\nLorsque votre connexion sera à nouveau chiffrée, vous recevrez une nouvelle notification."</string>
     <string name="scNullCipherIssueActionSettings" msgid="5888857706424639946">"Paramètres de sécurité du réseau mobile"</string>
     <string name="scNullCipherIssueActionLearnMore" msgid="7896642417214757769">"En savoir plus"</string>
@@ -202,6 +202,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Votre profil professionnel n\'est plus disponible sur cet appareil"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Trop de tentatives de saisie du mot de passe"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"L\'administrateur a mis l\'appareil à disposition pour un usage personnel"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Espace privé supprimé"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Votre organisation n\'autorise pas les espaces privés sur cet appareil géré."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"L\'appareil est géré"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Votre organisation gère cet appareil et peut surveiller le trafic réseau. Appuyez ici pour obtenir plus d\'informations."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Des applications peuvent accéder à votre position"</string>
@@ -2195,7 +2197,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Pavé directionnel - Gauche"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Pavé directionnel - Droite"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Pavé directionnel - Centre"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Barre de légende de l\'application <xliff:g id="APP_NAME">%1$s</xliff:g>."</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 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>
@@ -2416,14 +2417,12 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Fonctionnement"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"En attente…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Reconfigurer le déverrouillage par empreinte digitale"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
-    <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ne fonctionnait pas correctement et a été supprimée. Configurez-la à nouveau pour déverrouiller votre téléphone à l\'aide votre empreinte digitale."</string>
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> ne fonctionnait pas correctement et a été supprimée"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> et <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ne fonctionnaient pas correctement et ont été supprimées"</string>
+    <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ne fonctionnait pas correctement et a été supprimée. Configurez-la à nouveau pour déverrouiller votre téléphone à l\'aide de votre empreinte digitale."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> et <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ne fonctionnaient pas correctement et ont été supprimées. Configurez-les à nouveau pour déverrouiller votre téléphone à l\'aide de votre empreinte digitale."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Reconfigurer le déverrouillage par reconnaissance faciale"</string>
-    <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Votre empreinte faciale ne fonctionnait pas correctement et a été supprimée. Configurez-la à nouveau pour déverrouiller votre téléphone à l\'aide votre visage."</string>
+    <string name="face_dangling_notification_msg" msgid="8806849376915541655">"Votre empreinte faciale ne fonctionnait pas correctement et a été supprimée. Configurez-la à nouveau pour déverrouiller votre téléphone à l\'aide de votre visage."</string>
     <string name="biometric_dangling_notification_action_set_up" msgid="8246885009807817961">"Configuration"</string>
     <string name="biometric_dangling_notification_action_not_now" msgid="8095249216864443491">"Pas maintenant"</string>
 </resources>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 1808743..cb8e804 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"O teu perfil de traballo xa non está dispoñible neste dispositivo"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Demasiados intentos de introdución do contrasinal"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"O administrador renunciou ao dispositivo para uso persoal"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Quitouse o espazo privado"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"A túa organización non permite espazos privados neste dispositivo xestionado."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"O dispositivo está xestionado"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"A túa organización xestiona este dispositivo e pode controlar o tráfico de rede. Toca para obter máis detalles."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"As aplicacións poden acceder á túa localización"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Cruceta: esquerda"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Cruceta: dereita"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Cruceta: centro"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Barra de subtítulos de <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Como funciona?"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configura de novo o desbloqueo dactilar"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"A <xliff:g id="FINGERPRINT">%s</xliff:g> non funcionaba correctamente, polo que se eliminou"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"As impresións dixitais <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> non funcionaban correctamente, polo que se eliminaron"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"A <xliff:g id="FINGERPRINT">%s</xliff:g> non funcionaba correctamente, polo que se eliminou. Configúraa de novo para desbloquear o teléfono usando a impresión dixital."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"As impresións dixitais <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> non funcionaban correctamente, polo que se eliminaron. Configúraas de novo para desbloquear o teléfono usando a impresión dixital."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Configura de novo o desbloqueo facial"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index f9b6ae3..55032c9 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"આ ઉપકરણ પર તમારી કાર્યાલયની પ્રોફાઇલ હવે ઉપલબ્ધ નથી"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"પાસવર્ડના ઘણા વધુ પ્રયત્નો"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"વ્યવસ્થાપકે ડિવાઇસ વ્યક્તિગત ઉપયોગ માટે આપી દીધું છે"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"ખાનગી સ્પેસ કાઢી નાખી"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"મેનેજ કરેલા ડિવાઇસ પર, તમારી સંસ્થા દ્વારા ખાનગી સ્પેસને મંજૂરી આપવામાં આવતી નથી."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"ડિવાઇસ મેનેજ થયેલ છે"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"તમારી સંસ્થા આ ઉપકરણનું સંચાલન કરે છે અને નેટવર્ક ટ્રાફિફનું નિયમન કરી શકે છે. વિગતો માટે ટૅપ કરો."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"ઍપ તમારા સ્થાનને ઍક્સેસ કરી શકે છે"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"ડી-પૅડ ડાબે"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"ડી-પૅડ જમણે"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"ડી-પૅડ મધ્યમાં"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g>નું કૅપ્શન બાર."</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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"તેની કામ કરવાની રીત"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"બાકી..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ફિંગરપ્રિન્ટ અનલૉક સુવિધાનું ફરી સેટઅપ કરો"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> યોગ્ય રીતે કામ કરતી ન હતી અને તેને ડિલીટ કરવામાં આવી હતી"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> અને <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> યોગ્ય રીતે કામ કરતી ન હતી અને તેને ડિલીટ કરવામાં આવી હતી"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> બરાબર કામ કરતી ન હતી અને તેને ડિલીટ કરવામાં આવી હતી. તમારા ફોનને ફિંગરપ્રિન્ટ વડે અનલૉક કરવા માટે, તેનું ફરીથી સેટઅપ કરો."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> અને <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> બરાબર કામ કરતી ન હતી અને તેને ડિલીટ કરવામાં આવી હતી. તમારા ફોનને તમારી ફિંગરપ્રિન્ટ વડે અનલૉક કરવા માટે, તેનું ફરીથી સેટઅપ કરો."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"ફેસ અનલૉક સુવિધાનું ફરી સેટઅપ કરો"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index ae9b17c..e47715a 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"आपकी वर्क प्रोफ़ाइल अब इस डिवाइस पर उपलब्‍ध नहीं है"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"कई बार गलत पासवर्ड डाला गया"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"एडमिन ने निजी इस्तेमाल के लिए डिवाइस दे दिया है"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"प्राइवेट स्पेस हटाया गया"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"आपका संगठन मैनेज किए जा रहे इस डिवाइस पर प्राइवेट स्पेस रखने की अनुमति नहीं देता है."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"डिवाइस प्रबंधित है"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"आपका संगठन इस डिवाइस का प्रबंधन करता है और वह नेटवर्क ट्रैफ़िक की निगरानी भी कर सकता है. विवरण के लिए टैप करें."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"ऐप्लिकेशन आपकी जगह की जानकारी ऐक्सेस कर सकते हैं"</string>
@@ -1188,8 +1190,8 @@
     <string name="deleteText" msgid="4200807474529938112">"मिटाएं"</string>
     <string name="inputMethod" msgid="1784759500516314751">"इनपुट विधि"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"लेख क्रियाएं"</string>
-    <string name="error_handwriting_unsupported" msgid="7809438534946014050">"इस फ़ील्ड में हैंडराइटिंग की सुविधा मौजूद नहीं है"</string>
-    <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"पासवर्ड वाले फ़ील्ड में हैंडराइटिंग की सुविधा मौजूद नहीं है"</string>
+    <string name="error_handwriting_unsupported" msgid="7809438534946014050">"हैंडराइटिंग की सुविधा, इस फ़ील्ड में काम नहीं करती है"</string>
+    <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"हैंडराइटिंग की सुविधा, पासवर्ड वाले फ़ील्ड में काम नहीं करती है"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"वापस जाएं"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"इनपुट का तरीका बदलें"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"मेमोरी में जगह नहीं बची है"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"डी-पैड का बाईं ओर वाला बटन"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"डी-पैड का दाईं ओर वाला बटन"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"डी-पैड का बीच वाला बटन"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> का कैप्शन बार."</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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"यह सेटिंग कैसे काम करती है"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"प्रोसेस जारी है..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"फ़िंगरप्रिंट अनलॉक की सुविधा दोबारा सेट अप करें"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"अच्छे से काम न करने की वजह से <xliff:g id="FINGERPRINT">%s</xliff:g> को मिटा दिया गया"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"अच्छे से काम न करने की वजह से, <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> और <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> को मिटा दिया गया"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"अच्छे से काम न करने की वजह से <xliff:g id="FINGERPRINT">%s</xliff:g> को मिटा दिया गया. फ़िंगरप्रिंट की मदद से फ़ोन अनलॉक करने के लिए, फ़िंगरप्रिंट अनलॉक की सुविधा को दोबारा सेट अप करें."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"अच्छे से काम न करने की वजह से, <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> और <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> को मिटा दिया गया. फ़िंगरप्रिंट की मदद से फ़ोन अनलॉक करने के लिए, फ़िंगरप्रिंट अनलॉक की सुविधा दोबारा सेट अप करें."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"फ़ेस अनलॉक की सुविधा को दोबारा सेट अप करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 0bd8be3..0b9b662 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -202,6 +202,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Vaš poslovni profil više nije dostupan na ovom uređaju"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Previše pokušaja unosa zaporke"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administrator je ustupio uređaj za osobnu upotrebu"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Privatni prostor je uklonjen"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Vaša organizacija ne dopušta privatne prostore na ovom upravljanom uređaju."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Uređaj je upravljan"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Vaša organizacija upravlja ovim uređajem i može nadzirati mrežni promet. Dodirnite za pojedinosti."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikacije mogu pristupiti vašoj lokaciji"</string>
@@ -284,8 +286,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Glasovna pomoć"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Zaključaj"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Odgovor"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Nova obavijest"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fizička tipkovnica"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Sigurnost"</string>
@@ -2196,7 +2197,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Lijevo na plohi za smjerove"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Desno na plohi za smjerove"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"U središtu plohe za smjerove"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Traka naslova aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
@@ -2417,10 +2417,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kako to funkcionira"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Na čekanju..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ponovno postavite otključavanje otiskom prsta"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"Otisak prsta <xliff:g id="FINGERPRINT">%s</xliff:g> nije dobro funkcionirao i izbrisan je"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"Otisci prstiju <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nisu dobro funkcionirali i izbrisani su"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Otisak prsta <xliff:g id="FINGERPRINT">%s</xliff:g> nije dobro funkcionirao i izbrisan je. Ponovno ga postavite da biste otključali telefon otiskom prsta."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Otisci prstiju <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nisu dobro funkcionirali i izbrisani su. Ponovno ih postavite da biste otključali telefon otiskom prsta."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Ponovno postavite otključavanje licem"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index aeebbdc..de6777e 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Munkaprofilja már nem hozzáférhető ezen az eszközön."</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Túl sok jelszómegadási kísérlet"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Az adminisztrátor átadta az eszközt személyes használatra"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Privát terület eltávolítva"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"A szervezete nem engedélyez privát területeket ezen a kezelt eszközön."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Felügyelt eszköz"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Ezt az eszközt szervezete kezeli, és lehetséges, hogy a hálózati forgalmat is figyelik. További részletekért koppintson."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Az alkalmazások hozzáférhetnek az Ön tartózkodási helyéhez"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Hangsegéd"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Zárolás"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Válasz"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Új értesítés"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fizikai billentyűzet"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Biztonság"</string>
@@ -2195,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"D-pad – balra"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"D-pad – jobbra"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"D-pad – középre"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> alkalmazás címsora."</string>
     <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>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Hogyan működik?"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Függőben…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"A Feloldás ujjlenyomattal funkció újbóli beállítása"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"A(z) <xliff:g id="FINGERPRINT">%s</xliff:g> nem működött megfelelően, ezért törölve lett"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"A(z) <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> és a(z) <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nem működtek megfelelően, ezért törölve lettek"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"A(z) <xliff:g id="FINGERPRINT">%s</xliff:g> nem működött megfelelően, ezért törölve lett. Állítsa be újra, hogy feloldhassa a telefonját az ujjlenyomata segítségével."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"A(z) <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> és a(z) <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nem működtek megfelelően, ezért törölve lettek. Állítsa be őket újra, hogy feloldhassa a telefonját az ujjlenyomata segítségével."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Állítsa be újra az Arcalapú feloldást"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index a0e0bd2..58331c7 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Ձեր աշխատանքային պրոֆիլն այս սարքում այլևս հասանելի չէ"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Գաղտնաբառը մուտքագրելու չափից շատ փորձեր են կատարվել"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Ադմինիստրատորը տրամադրել է սարքը անձնական օգտագործման համար"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Մասնավոր տարածքը հեռացվել է"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Ձեր կազմակերպությունն արգելում է մասնավոր տարածքներն այս կառավարվող սարքում։"</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Սարքը կառավարվում է"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Ձեր կազմակերպությունը կառավարում է այս սարքը և կարող է վերահսկել ցանցի թրաֆիկը: Հպեք՝ մանրամասները դիտելու համար:"</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Հավելվածներին հասանելի է ձեր տեղադրությունը"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Ձայնային օգնութ"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Արգելափակում"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Պատասխանել"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Նոր ծանուցում"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Ֆիզիկական ստեղնաշար"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Անվտանգություն"</string>
@@ -2195,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad-ի «Ձախ» կոճակ"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad-ի «Աջ» կոճակ"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad-ի «Կենտրոն» կոճակ"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի ենթագրերի գոտին։"</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>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ինչպես է դա աշխատում"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Առկախ է…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Նորից կարգավորեք մատնահետքով ապակողպումը"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"«<xliff:g id="FINGERPRINT">%s</xliff:g>» մատնահետքը հեռացվել է, քանի որ լավ չէր աշխատում"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"«<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>» և «<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>» մատնահետքերը հեռացվել են, քանի որ լավ չէին աշխատում"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"«<xliff:g id="FINGERPRINT">%s</xliff:g>» մատնահետքը հեռացվել է, քանի որ լավ չէր աշխատում։ Նորից կարգավորեք այն՝ ձեր հեռախոսը մատնահետքով ապակողպելու համար։"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"«<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>» և «<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>» մատնահետքերը հեռացվել են, քանի որ լավ չէին աշխատում։ Նորից կարգավորեք դրանք՝ ձեր հեռախոսը մատնահետքով ապակողպելու համար։"</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Նորից կարգավորեք դեմքով ապակողպումը"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index fb6180b..eeaf2b6 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Profil kerja tidak tersedia lagi di perangkat ini"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Terlalu banyak kesalahan sandi"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Admin melepaskan perangkat untuk penggunaan pribadi"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Ruang privasi dihapus"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Organisasi Anda tidak mengizinkan adanya ruang privasi di perangkat terkelola ini."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Perangkat ini ada yang mengelola"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Organisasi mengelola perangkat ini dan mungkin memantau traffic jaringan. Ketuk untuk melihat detailnya."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikasi dapat mengakses lokasi Anda"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Bantuan Suara"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Kunci total"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Balas"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Notifikasi baru"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Keyboard fisik"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Keamanan"</string>
@@ -2195,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad Kiri"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad Kanan"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad Tengah"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Kolom teks <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cara kerjanya"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Tertunda..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Siapkan Buka dengan Sidik Jari lagi"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> tidak berfungsi dengan baik dan telah dihapus"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> dan <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> tidak berfungsi dengan baik dan telah dihapus"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> tidak berfungsi dengan baik dan telah dihapus. Siapkan lagi untuk membuka kunci ponsel Anda dengan sidik jari."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> dan <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> tidak berfungsi dengan baik dan telah dihapus. Siapkan lagi untuk membuka kunci ponsel Anda dengan sidik jari."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Siapkan Buka dengan Wajah lagi"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 5b38461..7e68a06 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Vinnusniðið þitt er ekki lengur í boði á þessu tæki"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Of margar tilraunir til að slá inn aðgangsorð"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Kerfisstjóri lét af hendi tæki til einkanota"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Leynirými fjarlægt"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Fyrirtækið þitt leyfir ekki leynirými í þessu stýrða tæki."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Tækinu er stjórnað"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Fyrirtækið þitt stjórnar þessu tæki og kann að fylgjast með netnotkun. Ýttu hér til að fá upplýsingar."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Forrit hafa aðgang að staðsetningu þinni"</string>
@@ -2194,14 +2196,13 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Vinstrihnappur stýriflatar"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Hægrihnappur stýriflatar"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Miðjuhnappur stýriflatar"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Skjátextastika <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
     <string name="conversation_title_fallback_one_to_one" msgid="1980753619726908614">"Samtal"</string>
     <string name="conversation_title_fallback_group_chat" msgid="456073374993104303">"Hópsamtal"</string>
     <string name="unread_convo_overflow" msgid="920517615597353833">"<xliff:g id="MAX_UNREAD_COUNT">%1$d</xliff:g>+"</string>
-    <string name="resolver_personal_tab" msgid="2051260504014442073">"Persónulegt"</string>
+    <string name="resolver_personal_tab" msgid="2051260504014442073">"Einkasnið"</string>
     <string name="resolver_work_tab" msgid="2690019516263167035">"Vinna"</string>
     <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"Persónulegt yfirlit"</string>
     <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"Vinnuyfirlit"</string>
@@ -2395,7 +2396,7 @@
     <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Lyklaskipan er stillt á <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Ýttu til að breyta."</string>
     <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Vélbúnaðarlyklaborð eru stillt"</string>
     <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Ýttu til að sjá lyklaborð"</string>
-    <string name="profile_label_private" msgid="6463418670715290696">"Lokað"</string>
+    <string name="profile_label_private" msgid="6463418670715290696">"Leynirými"</string>
     <string name="profile_label_clone" msgid="769106052210954285">"Afrit"</string>
     <string name="profile_label_work" msgid="3495359133038584618">"Vinna"</string>
     <string name="profile_label_work_2" msgid="4691533661598632135">"Vinna 2"</string>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Svona virkar þetta"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Í bið…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Setja upp fingrafarskenni aftur"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> virkaði illa og var eytt."</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> og <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> virkuðu illa og var eytt."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> virkaði illa og var eytt. Settu það upp aftur til að taka símann úr lás með fingrafari."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> og <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> virkuðu illa og var eytt. Settu þau upp aftur til að taka símann úr lás með fingrafarinu þínu."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Setja upp andlitskenni aftur"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index f22b653..037e148 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -202,6 +202,10 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Il tuo profilo di lavoro non è più disponibile sul dispositivo"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Troppi tentativi di inserimento della password"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"L\'amministratore ha abbandonato il dispositivo per uso personale"</string>
+    <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) -->
+    <skip />
+    <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="554983187553845004">"Il dispositivo è gestito"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Questo dispositivo è gestito dalla tua organizzazione, che potrebbe monitorare il traffico di rete. Tocca per i dettagli."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Le app possono accedere alla tua posizione"</string>
@@ -284,8 +288,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Voice Assist"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Blocco"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Rispondi"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Nuova notifica"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Tastiera fisica"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Sicurezza"</string>
@@ -1903,7 +1906,7 @@
     <string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"Richiedi il PIN per lo sblocco"</string>
     <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"</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_deleted_device_owner" msgid="2292335928930293023">"Eliminato dall\'amministratore"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
@@ -2196,7 +2199,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"D-pad - Sinistra"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"D-pad - Destra"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"D-pad - Centro"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Barra del titolo di <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
@@ -2417,10 +2419,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Come funziona"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"In attesa…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Riconfigura lo Sblocco con l\'Impronta"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> non funzionava bene ed è stata eliminata"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> non funzionavano bene e sono state eliminate"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> non funzionava bene ed è stata eliminata. Riconfigurala per sbloccare lo smartphone con l\'impronta."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> non funzionavano bene e sono state eliminate. Riconfigurale per sbloccare lo smartphone con l\'impronta."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Riconfigura lo Sblocco con il Volto"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 3a924e1..a1e72da 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -202,6 +202,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"פרופיל העבודה שלך אינו זמין עוד במכשיר הזה"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"בוצעו ניסיונות רבים מדי להזנת סיסמה"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"מנהל המערכת ביטל את האפשרות לשימוש במכשיר לצרכים אישיים"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"המרחב הפרטי הוסר"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"הארגון שלך לא מאפשר שימוש במרחבים פרטיים במכשיר המנוהל הזה."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"המכשיר מנוהל"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"הארגון שלך מנהל את המכשיר הזה והוא עשוי לנטר את התנועה ברשת. יש להקיש לקבלת פרטים."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"לאפליקציות יש הרשאת גישה למיקום שלך"</string>
@@ -284,8 +286,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"האסיסטנט"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"נעילה"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"תשובה"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"התראה חדשה"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"מקלדת פיזית"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"אבטחה"</string>
@@ -2196,7 +2197,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"‏לחצן שמאלי ב-Dpad"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"‏לחצן ימני ב-Dpad"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"‏לחצן אמצעי ב-Dpad"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"סרגל כיתוב של <xliff:g id="APP_NAME">%1$s</xliff:g>."</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>
@@ -2397,7 +2397,7 @@
     <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"פריסת המקלדת מוגדרת ל<xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… אפשר להקיש כדי לשנות את ההגדרה הזו."</string>
     <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"הוגדרו מקלדות פיזיות"</string>
     <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"יש להקיש כדי להציג את המקלדות"</string>
-    <string name="profile_label_private" msgid="6463418670715290696">"פרופיל פרטי"</string>
+    <string name="profile_label_private" msgid="6463418670715290696">"פרטי"</string>
     <string name="profile_label_clone" msgid="769106052210954285">"שכפול"</string>
     <string name="profile_label_work" msgid="3495359133038584618">"פרופיל עבודה"</string>
     <string name="profile_label_work_2" msgid="4691533661598632135">"פרופיל עבודה 2"</string>
@@ -2417,10 +2417,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"איך זה עובד"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"בהמתנה..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"הגדרה חוזרת של \'ביטול הנעילה בטביעת אצבע\'"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"‫<xliff:g id="FINGERPRINT">%s</xliff:g> לא פעלה טוב ולכן היא נמחקה"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"‫<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ו<xliff:g id="FINGERPRINT_1">%2$s</xliff:g> לא פעלו טוב ולכן הן נמחקו"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"‫<xliff:g id="FINGERPRINT">%s</xliff:g> לא פעלה היטב ולכן היא נמחקה. עליך להגדיר אותה שוב כדי שתהיה לך אפשרות לבטל את הנעילה של הטלפון באמצעות טביעת אצבע."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"‫<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ו<xliff:g id="FINGERPRINT_1">%2$s</xliff:g> לא פעלו היטב ולכן הן נמחקו. עליך להגדיר אותן שוב כדי שתהיה לך אפשרות לבטל את הנעילה של הטלפון באמצעות טביעת אצבע."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"הגדרה חוזרת של \'פתיחה ע\"י זיהוי הפנים\'"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index a802d90..04e47a7 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"お使いの仕事用プロファイルはこのデバイスで使用できなくなりました"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"パスワード入力回数が上限に達しました"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"管理者により、デバイスの個人使用が許可されました"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"プライベート スペースの削除"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"この管理対象デバイス上のプライベート スペースは組織で許可されていません。"</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"管理対象のデバイス"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"このデバイスは組織によって管理され、ネットワーク トラフィックが監視される場合があります。詳しくはタップしてください。"</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"アプリに位置情報へのアクセスを許可しました"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"D-pad: 左"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"D-pad: 右"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"D-pad: 中央"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> のキャプション バーです。"</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>
@@ -2395,7 +2396,7 @@
     <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"キーボードのレイアウトは<xliff:g id="LAYOUT_1">%1$s</xliff:g>、<xliff:g id="LAYOUT_2">%2$s</xliff:g>、<xliff:g id="LAYOUT_3">%3$s</xliff:g>などに設定されています。タップで変更できます。"</string>
     <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"物理キーボードの設定完了"</string>
     <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"タップするとキーボードを表示できます"</string>
-    <string name="profile_label_private" msgid="6463418670715290696">"非公開"</string>
+    <string name="profile_label_private" msgid="6463418670715290696">"プライベート"</string>
     <string name="profile_label_clone" msgid="769106052210954285">"複製"</string>
     <string name="profile_label_work" msgid="3495359133038584618">"仕事用"</string>
     <string name="profile_label_work_2" msgid="4691533661598632135">"仕事用 2"</string>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"仕組み"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"保留中..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"指紋認証をもう一度設定してください"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> は正常に機能せず、削除されました"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> と <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> は正常に機能せず、削除されました"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> が正常に機能せず、削除されました。指紋認証でスマートフォンのロックを解除するには、設定し直してください。"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> と <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> が正常に機能せず、削除されました。指紋認証でスマートフォンのロックを解除するには、設定し直してください。"</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"顔認証をもう一度設定してください"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 338ec1c..4f87855 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"თქვენი სამსახურის პროფილი აღარ არის ხელმისაწვდომი ამ მოწყობილობაზე"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"დაფიქსირდა პაროლის შეყვანის ზედმეტად ბევრი მცდელობა"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"ადმინისტრატორმა გაათავისუფლა მოწყობილობა პირადი გამოყენებისთვის"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"კერძო სივრცე ამოშლილია"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"თქვენი ორგანიზაცია არ უშვებს ამ მართულ მოწყობილობაზე პირად სივრცეებს."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"მოწყობილობა მართულია"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"ამ მოწყობილობას თქვენი ორგანიზაცია მართავს და მას ქსელის ტრაფიკის მონიტორინგი შეუძლია. შეეხეთ დამატებითი დეტალებისთვის."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"აპებს შეუძლია თქვენს მდებარეობაზე წვდომა"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad მარცხნივ"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad მარჯვნივ"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad ცენტრი"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ის სუბტიტრების ზოლი."</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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"მუშაობის პრინციპი"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"მომლოდინე..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ანაბეჭდით განბლოკვის ხელახლა დაყენება"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> კარგად არ მუშაობდა და წაიშალა"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> და <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> კარგად არ მუშაობდნენ და წაიშალა"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> კარგად არ მუშაობდა და წაიშალა. თავიდან დააყენეთ, რათა თქვენი ტელეფონი თითის ანაბეჭდის საშუალებით განბლოკოთ."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> და <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> კარგად არ მუშაობდნენ და წაიშალა. თავიდან დააყენეთ, რათა თქვენი ტელეფონი თითის ანაბეჭდის საშუალებით განბლოკოთ."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"დააყენეთ სახით განბლოკვა ხელახლა"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index e407c09..f36d9fc 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Жұмыс профиліңіз осы құрылғыда енді қолжетімді емес"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Құпия сөз көп рет қате енгізілді"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Әкімші құрылғыны жеке пайдалануға ұсынды."</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Құпия кеңістік өшірілді"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Ұйымыңыз басқаратын құрылғыда құпия кеңістік мүмкіндіктерін пайдалануға рұқсат етілмейді."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Құрылғы басқарылады"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Ұйымыңыз осы құрылғыны басқарады және желі трафигін бақылауы мүмкін. Мәліметтер алу үшін түртіңіз."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Қолданбалар геодерегіңізді пайдалана алады"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Дауыс көмекшісі"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Құлыптау"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Жауап беру"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Жаңа хабарландыру"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Физикалық пернетақта"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Қауіпсіздік"</string>
@@ -2195,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Сол жақ Dpad түймесі"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Оң жақ Dpad түймесі"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Ортаңғы Dpad түймесі"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасының жазу жолағы."</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>
@@ -2396,7 +2396,7 @@
     <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Пернетақта схемасы \"<xliff:g id="LAYOUT_1">%1$s</xliff:g>\", \"<xliff:g id="LAYOUT_2">%2$s</xliff:g>\", \"<xliff:g id="LAYOUT_3">%3$s</xliff:g>\" деп орнатылды… Өзгерту үшін түртіңіз."</string>
     <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Физикалық пернетақталар конфигурацияланды"</string>
     <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Пернетақталарды көру үшін түртіңіз."</string>
-    <string name="profile_label_private" msgid="6463418670715290696">"Жеке"</string>
+    <string name="profile_label_private" msgid="6463418670715290696">"Құпия"</string>
     <string name="profile_label_clone" msgid="769106052210954285">"Клон"</string>
     <string name="profile_label_work" msgid="3495359133038584618">"Жұмыс"</string>
     <string name="profile_label_work_2" msgid="4691533661598632135">"Жұмыс 2"</string>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Бұл қалай орындалады?"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Дайын емес…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Саусақ ізімен ашу функциясын қайта реттеу"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> саусақ ізі дұрыс істемегендіктен жойылды."</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"Саусақ іздері (<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> және <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>) дұрыс істемегендіктен жойылды."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> саусақ ізі дұрыс істемегендіктен жойылды. Телефонды саусақ ізімен ашу үшін оны қайта реттеңіз."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Саусақ іздері (<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> және <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>) дұрыс істемегендіктен жойылды. Телефонды саусақ ізімен ашу үшін оларды қайта реттеңіз."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Бет тану функциясын қайта реттеу"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index b021038..195eb13 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"កម្រងព័ត៌មាន​ការងារ​របស់អ្នក​លែងមាន​នៅលើ​ឧបករណ៍​នេះទៀត​ហើយ"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"ការព្យាយាមបញ្ចូលពាក្យសម្ងាត់ច្រើនដងពេកហើយ"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"អ្នកគ្រប់គ្រង​បានបោះបង់​ឧបករណ៍​ចោល​ដោយសារ​ការប្រើប្រាស់​ផ្ទាល់ខ្លួន"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"លំហឯកជនត្រូវបានដកចេញ"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"ស្ថាប័នរបស់អ្នកមិនអនុញ្ញាតលំហឯកជននៅលើឧបករណ៍ដែលស្ថិតក្រោមការគ្រប់គ្រងនេះទេ។"</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"ឧបករណ៍ស្ថិតក្រោមការគ្រប់គ្រង"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"ស្ថាប័នរបស់អ្នកគ្រប់គ្រងឧបករណ៍នេះ ហើយអាចនឹងតាមដានចរាចរណ៍បណ្តាញ។ ចុចដើម្បីទទួលបានព័ត៌មានលម្អិត។"</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"កម្មវិធី​អាច​ចូលប្រើ​ទីតាំង​របស់អ្នក​បាន"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ឆ្វេង"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad ស្ដាំ"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad កណ្ដាល"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"របារពណ៌នា​អំពី <xliff:g id="APP_NAME">%1$s</xliff:g>។"</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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"របៀបដែលវាដំណើរការ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"កំពុងរង់ចាំ..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"រៀបចំការដោះសោ​ដោយស្កេន​ស្នាមម្រាមដៃម្ដងទៀត"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> មិនដំណើរការល្អទេ ហើយត្រូវបានលុបចេញហើយ"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> និង <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> មិនដំណើរការល្អទេ ហើយត្រូវបានលុបចេញហើយ"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> មិនដំណើរការល្អទេ ហើយត្រូវបានលុបចេញហើយ។ រៀបចំវាម្ដងទៀត ដើម្បីដោះសោទូរសព្ទរបស់អ្នកដោយប្រើស្នាមម្រាមដៃ។"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> និង <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> មិនដំណើរការល្អទេ ហើយត្រូវបានលុបចេញហើយ។ រៀបចំវាម្ដងទៀត ដើម្បីដោះសោទូរសព្ទរបស់អ្នកដោយប្រើស្នាមម្រាមដៃ។"</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"រៀបចំ​ការដោះ​សោ​ដោយស្កេន​មុខម្ដងទៀត"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 535c125..073e448 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"ನಿಮ್ಮ ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್‌ ಈ ಸಾಧನದಲ್ಲಿ ಈಗ ಲಭ್ಯವಿಲ್ಲ"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"ಹಲವಾರು ಪಾಸ್‌ವರ್ಡ್ ಪ್ರಯತ್ನಗಳು"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"ವೈಯಕ್ತಿಕ ಬಳಕೆಗಾಗಿ ನಿರ್ವಾಹಕರು ತೊರೆದ ಸಾಧನ"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್ ಅನ್ನು ತೆಗೆದುಹಾಕಲಾಗಿದೆ"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"ಈ ನಿರ್ವಹಿಸಲಾದ ಸಾಧನದಲ್ಲಿ ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್‌ಗಳನ್ನು ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ಅನುಮತಿಸುವುದಿಲ್ಲ."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"ಸಾಧನವನ್ನು ನಿರ್ವಹಿಸಲಾಗುತ್ತಿದೆ"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"ನಿಮ್ಮ ಸಂಸ್ಥೆಯು ಈ ಸಾಧನವನ್ನು ನಿರ್ವಹಿಸುತ್ತದೆ ಮತ್ತು ಅದು ನೆಟ್‌ವರ್ಕ್ ಟ್ರಾಫಿಕ್ ಮೇಲೆ ಗಮನವಿರಿಸಬಹುದು. ವಿವರಗಳಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"ಆ್ಯಪ್‌ಗಳು ನಿಮ್ಮ ಸ್ಥಳವನ್ನು ಪ್ರವೇಶಿಸಬಹುದು"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ನ ಎಡಭಾಗದ ಬಟನ್"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad ನ ಬಲಭಾಗದ ಬಟನ್"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad ನ ಮಧ್ಯದ ಬಟನ್"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಆ್ಯಪ್‌ನ ಶೀರ್ಷಿಕೆಯ ಪಟ್ಟಿ."</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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ಇದು ಹೇಗೆ ಕೆಲಸ ಮಾಡುತ್ತದೆ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"ಬಾಕಿ ಉಳಿದಿದೆ..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್‌ಲಾಕ್ ಅನ್ನು ಮತ್ತೊಮ್ಮೆ ಸೆಟಪ್ ಮಾಡಿ"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> ಸರಿಯಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತಿರಲಿಲ್ಲ, ಹಾಗಾಗಿ ಅದನ್ನು ಅಳಿಸಲಾಗಿದೆ"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ಮತ್ತು <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ಸರಿಯಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತಿರಲಿಲ್ಲ, ಹಾಗಾಗಿ ಅವುಗಳನ್ನು ಅಳಿಸಲಾಗಿದೆ"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ಸರಿಯಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತಿಲ್ಲ ಹಾಗೂ ಅದನ್ನು ಅಳಿಸಲಾಗಿದೆ. ಫಿಂಗರ್‌ ಪ್ರಿಂಟ್ ಮೂಲಕ ನಿಮ್ಮ ಫೋನ್ ಅನ್ನು ಅನ್‌ಲಾಕ್ ಮಾಡಲು ಅದನ್ನು ಪುನಃ ಸೆಟಪ್‌ ಮಾಡಿ."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ಮತ್ತು <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ಸರಿಯಾಗಿ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತಿಲ್ಲ ಹಾಗೂ ಅವುಗಳನ್ನು ಅಳಿಸಲಾಗಿದೆ. ನಿಮ್ಮ ಫಿಂಗರ್‌ ಪ್ರಿಂಟ್ ಮೂಲಕ ನಿಮ್ಮ ಫೋನ್ ಅನ್‌ಲಾಕ್ ಮಾಡಲು ಅವುಗಳನ್ನು ಪುನಃ ಸೆಟಪ್‌ ಮಾಡಿ."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"ಫೇಸ್ ಅನ್‌ಲಾಕ್ ಅನ್ನು ಮತ್ತೊಮ್ಮೆ ಸೆಟಪ್ ಮಾಡಿ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 7a8d324..bbe8aef 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"직장 프로필을 이 기기에서 더 이상 사용할 수 없습니다."</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"비밀번호 입력을 너무 많이 시도함"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"관리자가 기기를 개인용으로 전환했습니다."</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"비공개 스페이스 삭제됨"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"조직에서는 이 관리 기기에서 비공개 스페이스를 허용하지 않습니다."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"관리되는 기기"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"조직에서 이 기기를 관리하며 네트워크 트래픽을 모니터링할 수도 있습니다. 자세한 내용을 보려면 탭하세요."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"앱에서 위치 정보에 액세스할 수 있음"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"음성 지원"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"잠금"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"답장"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"새 알림"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"물리적 키보드"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"보안"</string>
@@ -2195,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"방향 패드 왼쪽"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"방향 패드 오른쪽"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"방향 패드 가운데"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g>의 자막 표시줄입니다."</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>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"작동 방식"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"대기 중…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"지문 잠금 해제 다시 설정"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> 지문이 제대로 작동하지 않아 삭제되었습니다."</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> 및 <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> 지문이 제대로 작동하지 않아 삭제되었습니다."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g>이(가) 제대로 작동하지 않아 삭제되었습니다. 지문으로 휴대전화를 잠금 해제하려면 다시 설정하세요."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> 및 <xliff:g id="FINGERPRINT_1">%2$s</xliff:g>이(가) 제대로 작동하지 않아 삭제되었습니다. 지문으로 휴대전화를 잠금 해제하려면 다시 설정하세요."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"얼굴 인식 잠금 해제 다시 설정"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 8a233a3..9686a14 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Жумуш профилиңиз бул түзмөктөн өчүрүлдү"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Өтө көп жолу сырсөздү киргизүү аракети жасалды"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Админ түзмөктөн жеке колдонуу үчүн баш тартты"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Жеке мейкиндик өчүрүлдү"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Уюмуңуз бул көзөмөлдөнгөн түзмөктө жеке мейкиндиктерди колдонууга тыюу салат."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Түзмөктү ишкана башкарат"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Ишканаңыз бул түзмөктү башкарат жана тармак трафигин көзөмөлдөшү мүмкүн. Чоо-жайын билгиңиз келсе, таптап коюңуз."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Колдонмолор кайда жүргөнүңүздү көрө алат"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Үн жардамчысы"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Бекем кулпулоо"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Жооп берүү"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Жаңы эскертме"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Аппараттык баскычтоп"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Коопсуздук"</string>
@@ -1189,8 +1190,8 @@
     <string name="deleteText" msgid="4200807474529938112">"Өчүрүү"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Киргизүү ыкмасы"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Текст боюнча иштер"</string>
-    <string name="error_handwriting_unsupported" msgid="7809438534946014050">"Бул талаада жазып киргизүү колдоого алынбайт"</string>
-    <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Сырсөз талаасында жазып киргизүү колдоого алынбайт"</string>
+    <string name="error_handwriting_unsupported" msgid="7809438534946014050">"Бул жерге кол менен жазганга болбойт"</string>
+    <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Сырсөз киргизилүүчү жерге кол менен жаза албайсыз"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Артка"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Киргизүү ыкмасын өзгөртүү"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Сактагычта орун калбай баратат"</string>
@@ -2195,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad\'дын сол баскычы"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad\'дын оң баскычы"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad\'дын ортоңку баскычы"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосунун маалымат тилкеси."</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>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ал кантип иштейт"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Кезекте турат..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Манжа изи менен ачуу функциясын кайра тууралаңыз"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> ойдогудай иштебегендиктен өчүрүлдү"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> жана <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ойдогудай иштебегендиктен өчүрүлдү"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ойдогудай иштебегендиктен, жок кылынды. Телефондо Манжа изи менен ачуу функциясын колдонуу үчүн аны кайра тууралаңыз."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> жана <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ойдогудай иштебегендиктен, жок кылынды. Телефонду манжа изи менен ачуу үчүн аларды кайра тууралаңыз."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Жүзүнөн таанып ачуу функциясын кайрадан тууралаңыз"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index ec583038..b6bd4b2 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກຂອງທ່ານບໍ່ສາມາດໃຊ້ໄດ້ໃນອຸປະກອນນີ້ອີກຕໍ່ໄປ"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"ລອງໃສ່ລະຫັດຜ່ານຫຼາຍເທື່ອເກີນໄປ"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"ອຸປະກອນທີ່ຍົກເລີກແລ້ວສຳລັບການໃຊ້ສ່ວນບຸກຄົນ"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"ລຶບພື້ນທີ່ສ່ວນບຸກຄົນອອກແລ້ວ"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"ອົງກອນຂອງທ່ານບໍ່ອະນຸຍາດໃຫ້ມີພື້ນທີ່ສ່ວນບຸກຄົນໃນອຸປະກອນທີ່ໄດ້ຮັບການຈັດການນີ້."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"ອຸປະກອນມີການຈັດການ"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"ອົງກອນຂອງທ່ານຈັດການອຸປະກອນນີ້ ແລະ ອາດກວດສອບທຣາບຟິກເຄືອຂ່າຍນຳ. ແຕະເພື່ອເບິ່ງລາຍລະອຽດ."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"ແອັບສາມາດເຂົ້າເຖິງສະຖານທີ່ຂອງທ່ານໄດ້"</string>
@@ -1188,8 +1190,8 @@
     <string name="deleteText" msgid="4200807474529938112">"ລຶບ"</string>
     <string name="inputMethod" msgid="1784759500516314751">"ຮູບແບບການປ້ອນຂໍ້ມູນ"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"ການເຮັດວຽກຂອງຂໍ້ຄວາມ"</string>
-    <string name="error_handwriting_unsupported" msgid="7809438534946014050">"ການຂຽນດ້ວຍມືບໍ່ຖືກຮອງຮັບໃນຊ່ອງຂໍ້ມູນນີ້"</string>
-    <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"ການຂຽນດ້ວຍມືບໍ່ຖືກຮອງຮັບໃນຊ່ອງຂໍ້ມູນລະຫັດຜ່ານ"</string>
+    <string name="error_handwriting_unsupported" msgid="7809438534946014050">"ບໍ່ຮອງຮັບການຂຽນດ້ວຍມືໃນຊ່ອງຂໍ້ມູນນີ້"</string>
+    <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"ຊ່ອງຂໍ້ມູນລະຫັດຜ່ານບໍ່ຮອງຮັບການຂຽນດ້ວຍມື"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"ກັບຄືນ"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"ສະຫຼັບວິທີການປ້ອນຂໍ້ມູນ"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"ພື້ນທີ່ຈັດເກັບຂໍ້ມູນກຳລັງຈະເຕັມ"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ຊ້າຍ"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad ຂວາ"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad ກາງ"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"ແຖບຄຳບັນຍາຍຂອງ <xliff:g id="APP_NAME">%1$s</xliff:g>."</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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ມັນເຮັດວຽກແນວໃດ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"ລໍຖ້າດຳເນີນການ..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ຕັ້ງຄ່າການປົດລັອກດ້ວຍລາຍນິ້ວມືຄືນໃໝ່"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> ເຮັດວຽກໄດ້ບໍ່ດີ ແລະ ຖືກລຶບອອກແລ້ວ"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ແລະ <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ເຮັດວຽກໄດ້ບໍ່ດີ ແລະ ຖືກລຶບອອກແລ້ວ"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ເຮັດວຽກໄດ້ບໍ່ດີ ແລະ ຖືກລຶບອອກແລ້ວ. ໃຫ້ຕັ້ງຄ່າມັນຄືນໃໝ່ເພື່ອປົດລັອກໂທລະສັບຂອງທ່ານດ້ວຍລາຍນິ້ວມື."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ແລະ <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ເຮັດວຽກໄດ້ບໍ່ດີ ແລະ ຖືກລຶບອອກແລ້ວ. ໃຫ້ຕັ້ງຄ່າພວກມັນຄືນໃໝ່ເພື່ອປົດລັອກໂທລະສັບຂອງທ່ານດ້ວຍລາຍນິ້ວມື."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"ຕັ້ງຄ່າການປົດລັອກດ້ວຍໜ້າຄືນໃໝ່"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index d3bf781..2b4f370 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -203,6 +203,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Darbo profilis nebepasiekiamas šiame įrenginyje"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Per daug slaptažodžio bandymų"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administratorius atmetė prašymą įrenginį naudoti asmeniniais tikslais"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Privati erdvė pašalinta"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Jūsų organizacija neleidžia naudoti privačių erdvių šiame tvarkomame įrenginyje."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Įrenginys yra tvarkomas"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Šį įrenginį tvarko organizacija ir gali stebėti tinklo srautą. Palieskite, kad gautumėte daugiau informacijos."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Programos gali pasiekti jūsų vietovę"</string>
@@ -2196,7 +2198,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Valdymo pultas – kairėn"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Valdymo pultas – dešinėn"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Valdymo pultas – centras"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Programos „<xliff:g id="APP_NAME">%1$s</xliff:g>“ antraštės juosta."</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>
@@ -2417,10 +2418,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kaip tai veikia"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Laukiama..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Atrakinimo piršto atspaudu nustatymas dar kartą"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> neveikė tinkamai ir buvo ištrintas"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ir <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> neveikė tinkamai ir buvo ištrinti"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> neveikė tinkamai ir buvo ištrintas. Nustatykite jį dar kartą, kad atrakintumėte telefoną piršto atspaudu."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ir <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> neveikė tinkamai ir buvo ištrinti. Nustatykite juos dar kartą, kad atrakintumėte telefoną piršto atspaudu."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Atrakinimo pagal veidą nustatymas iš naujo"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 8be327d..3669f04 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -202,6 +202,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Jūsu darba profils šai ierīcē vairs nav pieejams."</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Veikts pārāk daudz paroles ievadīšanas mēģinājumu."</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administrators atteicās no tādas ierīces pārvaldības, ko var izmantot personiskām vajadzībām"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Privātā telpa ir noņemta"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Jūsu organizācija neatļauj izmantot privātās telpas šajā pārvaldītajā ierīcē."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Ierīce tiek pārvaldīta"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Jūsu organizācija pārvalda šo ierīci un var uzraudzīt tīkla datplūsmu. Pieskarieties, lai saņemtu detalizētu informāciju."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Lietotne var piekļūt jūsu atrašanās vietas datiem"</string>
@@ -284,8 +286,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Balss palīgs"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Bloķēšana"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"Pārsniedz"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Atbildēt"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Jauns paziņojums"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fiziskā tastatūra"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Drošība"</string>
@@ -2196,14 +2197,13 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Virzienu slēdzis — pa kreisi"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Virzienu slēdzis — pa labi"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Virzienu slēdzis — centrs"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Lietotnes <xliff:g id="APP_NAME">%1$s</xliff:g> subtitru josla."</string>
     <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>
     <string name="conversation_title_fallback_one_to_one" msgid="1980753619726908614">"Saruna"</string>
     <string name="conversation_title_fallback_group_chat" msgid="456073374993104303">"Grupas saruna"</string>
     <string name="unread_convo_overflow" msgid="920517615597353833">"<xliff:g id="MAX_UNREAD_COUNT">%1$d</xliff:g>+"</string>
-    <string name="resolver_personal_tab" msgid="2051260504014442073">"Privātais profils"</string>
+    <string name="resolver_personal_tab" msgid="2051260504014442073">"Personīgais"</string>
     <string name="resolver_work_tab" msgid="2690019516263167035">"Darba profils"</string>
     <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"Personisks skats"</string>
     <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"Darba skats"</string>
@@ -2417,10 +2417,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Darbības principi"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Gaida…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Vēlreiz iestatiet autorizāciju ar pirksta nospiedumu"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> nedarbojās pareizi un tika izdzēsts"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> un <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nedarbojās pareizi un tika izdzēsti"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> nedarbojās pareizi un tika izdzēsts. Iestatiet to atkal, lai varētu atbloķēt tālruni ar pirksta nospiedumu."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> un <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nedarbojās pareizi un tika izdzēsti. Iestatiet tos atkal, lai varētu atbloķētu tālruni ar pirksta nospiedumu."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Vēlreiz iestatiet autorizāciju pēc sejas"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index d8acc30..8902619 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Вашиот работен профил веќе не е достапен на уредов"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Премногу обиди за внесување лозинка"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Уред откажан од администраторот за лична употреба"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"„Приватниот простор“ е отстранет"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Вашата организација не дозволува „Приватен простор“ на управуваниов уред."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Некој управува со уредот"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Вашата организација управува со уредов и можно е да го следи сообраќајот на мрежата. Допрете за детали."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Апликациите може да пристапуваат до вашата локација"</string>
@@ -1188,7 +1190,7 @@
     <string name="deleteText" msgid="4200807474529938112">"Избриши"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Метод на внес"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Дејства со текст"</string>
-    <string name="error_handwriting_unsupported" msgid="7809438534946014050">"Ракописот не е поддржан во полево"</string>
+    <string name="error_handwriting_unsupported" msgid="7809438534946014050">"Ракописот не е поддржан во ова поле"</string>
     <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Ракописот не е поддржан во полињата за лозинка"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Назад"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Префрлете го методот за внесување"</string>
@@ -2194,14 +2196,13 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Навигациско копче за налево"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Навигациско копче за надесно"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Навигациско копче за средина"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Насловна лента на <xliff:g id="APP_NAME">%1$s</xliff:g>."</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>
     <string name="conversation_title_fallback_one_to_one" msgid="1980753619726908614">"Разговор"</string>
     <string name="conversation_title_fallback_group_chat" msgid="456073374993104303">"Групен разговор"</string>
     <string name="unread_convo_overflow" msgid="920517615597353833">"<xliff:g id="MAX_UNREAD_COUNT">%1$d</xliff:g>+"</string>
-    <string name="resolver_personal_tab" msgid="2051260504014442073">"Лични"</string>
+    <string name="resolver_personal_tab" msgid="2051260504014442073">"Лично"</string>
     <string name="resolver_work_tab" msgid="2690019516263167035">"За работа"</string>
     <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"Личен приказ"</string>
     <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"Работен приказ"</string>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Дознајте како функционира"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Во фаза на чекање…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Поставете „Отклучување со отпечаток“ повторно"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> не функционираше добро, па се избриша"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> и <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> не функционираа добро, па се избришаа"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> не функционираше добро, па се избриша. Поставете го повторно за да го отклучувате телефонот со отпечаток."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> и <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> не функционираа добро, па се избришаа. Поставете ги повторно за да го отклучувате телефонот со отпечаток."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Поставете „Отклучување со лик“ повторно"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 747631a..005d7cb 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"ഈ ഉപകരണത്തിൽ തുടർന്നങ്ങോട്ട് നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈൽ ലഭ്യമല്ല"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"വളരെയധികം പാസ്‌വേഡ് ശ്രമങ്ങൾ"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"വ്യക്തിപരമായ ഉപയോഗത്തിനായി, ഉപകരണത്തിന്റെ ഔദ്യോഗിക ഉപയോഗം അഡ്‌മിൻ അവസാനിപ്പിച്ചു"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"സ്വകാര്യ സ്പേസ് നീക്കം ചെയ്തു"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"മാനേജ് ചെയ്യപ്പെടുന്ന ഈ ഉപകരണത്തിൽ നിങ്ങളുടെ സ്ഥാപനം സ്വകാര്യ സ്പേസുകൾ അനുവദിക്കുന്നില്ല."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"ഉപകരണം മാനേജുചെയ്യുന്നുണ്ട്"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"നിങ്ങളുടെ സ്ഥാപനമാണ് ഈ ഉപകരണം മാനേജുചെയ്യുന്നത്, നെറ്റ്‌വർക്ക് ട്രാഫിക്ക് നിരീക്ഷിക്കുകയും ചെയ്തേക്കാം, വിശദാംശങ്ങൾ അറിയാൻ ടാപ്പുചെയ്യുക."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"ആപ്പുകൾക്ക് നിങ്ങളുടെ ലൊക്കേഷൻ ആക്‌സസ് ചെയ്യാനാകും"</string>
@@ -2147,7 +2149,7 @@
     <string name="dynamic_mode_notification_summary" msgid="1639031262484979689">"ബാറ്ററി ലൈഫ് വർദ്ധിപ്പിക്കാൻ ബാറ്ററി ഉപയോഗം കുറയ്ക്കുന്നു"</string>
     <string name="dynamic_mode_notification_title_v2" msgid="5072385242078021152">"ബാറ്ററി സേവർ ഓണാണ്"</string>
     <string name="dynamic_mode_notification_summary_v2" msgid="2142444344663147938">"ബാറ്ററി ലൈഫ് വർദ്ധിപ്പിക്കാൻ ബാറ്ററി സേവർ ഓണാക്കിയിരിക്കുന്നു"</string>
-    <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"ബാറ്ററി ലാഭിക്കൽ"</string>
+    <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"ബാറ്ററി സേവർ"</string>
     <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"ബാറ്ററി സേവർ ഓഫാക്കിയിരിക്കുന്നു"</string>
     <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"ഫോണിൽ വേണ്ടത്ര ചാർജ് ഉണ്ട്. ഫീച്ചറുകൾക്ക് ഇനി നിയന്ത്രണമില്ല."</string>
     <string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"ടാബ്‌ലെറ്റിൽ വേണ്ടത്ര ചാർജ് ഉണ്ട്. ഫീച്ചറുകൾക്ക് ഇനി നിയന്ത്രണമില്ല."</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ലെഫ്റ്റ്"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad റൈറ്റ്"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad സെന്റർ"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> എന്നതിന്റെ അടിക്കുറിപ്പ് ബാർ."</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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ഇത് പ്രവർത്തിക്കുന്നത് എങ്ങനെയാണ്"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"തീർപ്പാക്കിയിട്ടില്ല..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ഫിംഗർപ്രിന്റ് അൺലോക്ക് വീണ്ടും സജ്ജീകരിക്കുക"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> ശരിയായി പ്രവർത്തിക്കാത്തതിനാൽ അത് ഇല്ലാതാക്കി"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>, <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> എന്നിവ ശരിയായി പ്രവർത്തിക്കാത്തതിനാൽ അവ ഇല്ലാതാക്കി"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ശരിയായി പ്രവർത്തിക്കാത്തതിനാൽ അത് ഇല്ലാതാക്കി. നിങ്ങളുടെ ഫിംഗർപ്രിന്റ് ഉപയോഗിച്ച് ഫോൺ അൺലോക്ക് ചെയ്യുന്നതിനായി വീണ്ടും സജ്ജീകരിക്കുക."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>, <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> എന്നിവ ശരിയായി പ്രവർത്തിക്കാത്തതിനാൽ അവ ഇല്ലാതാക്കി. നിങ്ങളുടെ ഫിംഗർപ്രിന്റ് ഉപയോഗിച്ച് ഫോൺ അൺലോക്ക് ചെയ്യുന്നതിനായി അവ വീണ്ടും സജ്ജീകരിക്കുക."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"ഫെയ്‌സ് അൺലോക്ക് വീണ്ടും സജ്ജീകരിക്കുക"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 8d430ee..ddbeb35 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Таны ажлын профайл энэ төхөөрөмжид боломжгүй байна"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Нууц үгийг хэт олон удаа буруу оруулсан байна"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Админ хувийн хэрэглээнд зориулж төхөөрөмжийн эрхийг хассан"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Хаалттай орон зайг хассан"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Танай байгууллага энэ удирддаг төхөөрөмж дээр хаалттай орон зайг зөвшөөрдөггүй."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Төхөөрөмжийг удирдсан"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Таны байгууллага энэ төхөөрөмжийг удирдаж, сүлжээний ачааллыг хянадаг. Дэлгэрэнгүй мэдээлэл авах бол товшино уу."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Аппууд нь таны байршилд хандах боломжтой"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Дуут туслах"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Түгжих"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Хариу бичих"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Шинэ мэдэгдэл"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Биет гар"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Аюулгүй байдал"</string>
@@ -1190,7 +1191,7 @@
     <string name="inputMethod" msgid="1784759500516314751">"Оруулах арга"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Текст үйлдэл"</string>
     <string name="error_handwriting_unsupported" msgid="7809438534946014050">"Гараар бичихийг энэ талбарт дэмждэггүй"</string>
-    <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Гараар бичихийг нууц үгний талбаруудад дэмждэггүй"</string>
+    <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"Нууц үгний талбарт гараар бичихийг дэмждэггүй"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Буцах"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Оруулах аргыг сэлгэх"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Сангийн хэмжээ дутагдаж байна"</string>
@@ -2195,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad зүүн"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad баруун"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad гол"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g>-н гарчгийн талбар."</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>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Энэ хэрхэн ажилладаг вэ?"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Хүлээгдэж буй..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Хурууны хээгээр түгжээ тайлахыг дахин тохируулна уу"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> сайн ажиллахгүй байсан тул хурууны хээг устгасан"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> болон <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> сайн ажиллахгүй байсан тул эдгээр хурууны хээг устгасан"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> сайн ажиллахгүй байсан тул үүнийг устгасан. Утасныхаа түгжээг хурууны хээгээр тайлахын тулд хурууны хээг дахин тохируулна уу."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>, <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> сайн ажиллахгүй байсан тул эдгээрийг устгасан. Утасныхаа түгжээг хурууныхаа хээгээр тайлахын тулд хоёр хурууны хээг дахин тохируулна уу."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Царайгаар түгжээ тайлахыг дахин тохируулна уу"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 022f5c3f..77a58af 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"तुमचे कार्य प्रोफाइल आता या डिव्हाइसवर उपलब्‍ध नाही"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"बर्‍याचदा पासवर्ड टाकण्‍याचा प्रयत्‍न केला"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"वैयक्तिक वापरासाठी ॲडमिनने नियंत्रण सोडलेले डिव्हाइस"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"खाजगी स्पेस काढून टाकली आहे"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"तुमची संस्था या व्यवस्थापित केलेल्या डिव्हाइसवर खाजगी स्पेसना अनुमती देत नाही."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"डिव्हाइस व्यवस्थापित केले आहे"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"तुमची संस्था हे डिव्हाइस व्यवस्थापित करते आणि नेटवर्क रहदारीचे निरीक्षण करू शकते. तपशीलांसाठी टॅप करा."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"ॲप्स तुमचे स्थान अ‍ॅक्सेस करू शकतात"</string>
@@ -1188,8 +1190,8 @@
     <string name="deleteText" msgid="4200807474529938112">"हटवा"</string>
     <string name="inputMethod" msgid="1784759500516314751">"इनपुट पद्धत"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"मजकूर क्रिया"</string>
-    <string name="error_handwriting_unsupported" msgid="7809438534946014050">"या फील्डमध्ये हस्तलेखनला सपोर्ट नाही"</string>
-    <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"पासवर्ड फील्डमध्ये हस्तलेखनला सपोर्ट नाही"</string>
+    <string name="error_handwriting_unsupported" msgid="7809438534946014050">"या फील्डमध्ये हस्तलेखनाला सपोर्ट नाही"</string>
+    <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"पासवर्ड फील्डमध्ये हस्तलेखनाला सपोर्ट नाही"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"मागे जा"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"इनपुट पद्धत स्विच करा"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"संचयन स्थान संपत आहे"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad डावीकडील"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad चे उजवीकडील"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad चे मधले"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> चा शीर्षक बार."</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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ते कसे काम करते"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"प्रलंबित आहे..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"फिंगरप्रिंट अनलॉक पुन्हा सेट करा"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> योग्यरीत्या काम करत नव्हती, त्यामुळे ती हटवली आहे"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> आणि <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> योग्यरीत्या काम करत नव्हत्या, त्यामुळे त्या हटवल्या आहेत"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"योग्यरीत्या काम करत नसल्यामुळे <xliff:g id="FINGERPRINT">%s</xliff:g> हटवले गेले आहे. तुमचे फिंगरप्रिंट वापरून फोन अनलॉक करण्यासाठी ते पुन्हा सेट करा."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"परफॉर्मन्समध्ये सुधारणा करण्यासाठी आणि योग्यरीत्या काम करत नसल्यामुळे <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> व <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> हटवली गेली आहेत. तुमचे फिंगरप्रिंट वापरून फोन अनलॉक करण्यासाठी ते पुन्हा सेट करा."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"फेस अनलॉक पुन्हा सेट करा"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 908f5a8..96691f6 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Profil kerja anda tidak lagi tersedia pada peranti ini"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Terlalu banyak percubaan kata laluan"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Pentadbir melepaskan peranti untuk kegunaan peribadi"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Ruang privasi dialih keluar"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Organisasi anda tidak membenarkan ruang privasi pada peranti terurus ini."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Peranti ini diurus"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Organisasi anda mengurus peranti ini dan mungkin memantau trafik rangkaian. Ketik untuk mendapatkan butiran."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Apl boleh mengakses lokasi anda"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad Kiri"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad Kanan"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad Tengah"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Bar kapsyen <xliff:g id="APP_NAME">%1$s</xliff:g>."</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>
@@ -2403,7 +2404,7 @@
     <string name="profile_label_test" msgid="9168641926186071947">"Ujian"</string>
     <string name="profile_label_communal" msgid="8743921499944800427">"Umum"</string>
     <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_private_profile" msgid="1436459319135548969">"Ruang persendirian"</string>
     <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string>
     <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Umum"</string>
     <string name="redacted_notification_message" msgid="1520587845842228816">"Kandungan pemberitahuan yang sensitif disembunyikan"</string>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cara ciri ini berfungsi"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Belum selesai..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Sediakan Buka Kunci Cap Jari sekali lagi"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> tidak berfungsi dengan baik dan telah dipadamkan"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> dan <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> tidak berfungsi dengan baik dan telah dipadamkan"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> tidak berfungsi dengan baik dan telah dipadamkan. Sediakan cap jari sekali lagi untuk membuka kunci telefon anda menggunakan cap jari."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> dan <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> tidak berfungsi dengan baik dan telah dipadamkan. Sediakan kedua-dua cap jari tersebut sekali lagi untuk membuka kunci telefon anda menggunakan cap jari anda."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Sediakan semula Buka Kunci Wajah"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 22fe7f2..c1b7822 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"ဤစက်ပစ္စည်းတွင် သင်၏ အလုပ်ပရိုဖိုင်မရှိတော့ပါ"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"စကားဝှက်ထည့်သွင်းရန် ကြိုးစားသည့် အကြိမ်အရေအတွက် အလွန်များသွား၍ ဖြစ်ပါသည်"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"ပုဂ္ဂိုလ်ရေးအသုံးပြုရန်အတွက် စီမံခန့်ခွဲသူက စက်ပစ္စည်းထိန်းချုပ်မှုကို ရပ်တန့်လိုက်သည်"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"သီးသန့်နေရာကို ဖယ်ရှားလိုက်သည်"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"သင့်အဖွဲ့အစည်းသည် ကြီးကြပ်ထားသော ဤစက်ပေါ်တွင် သီးသန့်နေရာများကို ခွင့်မပြုပါ။"</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"စက်ပစ္စည်းကို စီမံခန့်ခွဲထားပါသည်"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"ဤစက်ပစ္စည်းကို သင်၏ အဖွဲ့အစည်းက စီမံပြီး ကွန်ရက်အသွားအလာကို စောင့်ကြည့်နိုင်ပါသည်။ ထပ်မံလေ့လာရန် တို့ပါ။"</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"အက်ပ်များက သင်၏တည်နေရာကို ကြည့်နိုင်သည်"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ဘယ်"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad ညာ"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad အလယ်"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g>၏ ခေါင်းစီး ဘား။"</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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"အလုပ်လုပ်ပုံ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"ဆိုင်းငံ့ထားသည်…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"‘လက်ဗွေသုံး လော့ခ်ဖွင့်ခြင်း’ ကို စနစ်ထပ်မံထည့်သွင်းပါ"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> သိပ်အဆင်မပြေသဖြင့် ဖျက်ထားသည်"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> နှင့် <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> တို့ သိပ်အဆင်မပြေသဖြင့် ဖျက်ထားသည်"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> သိပ်အဆင်မပြေသဖြင့် ဖျက်ထားသည်။ သင့်ဖုန်းကို လက်ဗွေဖြင့်လော့ခ်ဖွင့်ရန် ၎င်းကို စနစ်ထပ်မံထည့်သွင်းပါ။"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> နှင့် <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> တို့ သိပ်အဆင်မပြေသဖြင့် ဖျက်ထားသည်။ သင့်ဖုန်းကို လက်ဗွေဖြင့်လော့ခ်ဖွင့်ရန် ၎င်းတို့ကို စနစ်ထပ်မံထည့်သွင်းပါ။"</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"‘မျက်နှာပြ လော့ခ်ဖွင့်ခြင်း’ ကို စနစ်ထပ်မံထည့်သွင်းပါ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 3939f48..23f675e 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Jobbprofilen din er ikke lenger tilgjengelig på denne enheten"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"For mange passordforsøk"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administratoren overførte enheten til personlig bruk"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Det private området er fjernet"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Organisasjonen din tillater ikke private områder på denne administrerte enheten."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Enheten administreres"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Organisasjonen din kontrollerer denne enheten og kan overvåke nettverkstrafikk. Trykk for å få mer informasjon."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Apper har tilgang til posisjonen din"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Talehjelp"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Låsing"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Svar"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Nytt varsel"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fysisk tastatur"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Sikkerhet"</string>
@@ -2195,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Venstre på styrepilene"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Høyre på styrepilene"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Midt på styrepilene"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Tekstingsfelt i <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Slik fungerer det"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Venter …"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Konfigurer opplåsingen med fingeravtrykk på nytt"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> fungerte ikke skikkelig og ble slettet"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> og <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> fungerte ikke skikkelig og ble slettet"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> fungerte ikke skikkelig og ble slettet. Du kan konfigurere det på nytt for å låse opp telefonen med fingeravtrykket."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> og <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> fungerte ikke skikkelig og ble slettet. Du kan konfigurere dem på nytt for å låse opp telefonen med fingeravtrykket."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Konfigurer ansiktslåsen på nytt"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 3253afa..99dd0e4 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"तपाईंको कार्य प्रोफाइल अब उप्रान्त यस डिभाइसमा उपलब्ध छैन"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"पासवर्ड प्रविष्ट गर्ने अत्यधिक गलत प्रयासहरू भए"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"व्यवस्थापकले यन्त्रलाई व्यक्तिगत प्रयोगका लागि अस्वीकार गर्नुभयो"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"निजी स्पेस हटाइएको छ"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"तपाईंको सङ्गठन आफूले व्यवस्थापन गरेको यो डिभाइसमा निजी स्पेस राख्ने अनुमति दिँदैन।"</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"यन्त्र व्यवस्थित गरिएको छ"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"तपाईंको संगठनले यस डिभाइसको व्यवस्थापन गर्दछ र नेटवर्क ट्राफिकको अनुगमन गर्न सक्छ। विवरणहरूका लागि ट्याप गर्नुहोस्।"</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"एपहरूले तपाईंको स्थान प्रयोग गर्न सक्छन्"</string>
@@ -1171,7 +1173,7 @@
     <string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="1532369154488982046">"सबैलाई चयन गर्नुहोस्"</string>
-    <string name="cut" msgid="2561199725874745819">"काट्नुहोस्"</string>
+    <string name="cut" msgid="2561199725874745819">"कट् गर्नुहोस्"</string>
     <string name="copy" msgid="5472512047143665218">"कपी गर्नुहोस्"</string>
     <string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"क्लिपबोर्डमा कपी गर्न सकिएन"</string>
     <string name="paste" msgid="461843306215520225">"टाँस्नुहोस्"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad को बायाँको बटन"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad को दायाँको बटन"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad को बिचको बटन"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> को क्याप्सन बार।"</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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"यसले काम गर्ने तरिका"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"विचाराधीन..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"फिंगरप्रिन्ट अनलक फेरि सेटअप गर्नुहोस्"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"राम्ररी काम नगरिरहेको हुनाले <xliff:g id="FINGERPRINT">%s</xliff:g> मेटाइएको छ"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"राम्ररी काम नगरिरहेका हुनाले <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> र <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> मेटाइएका छन्"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ले काम गरिरहेको थिएन र त्यसलाई मेटाइयो। फिंगरप्रिन्ट प्रयोग गरी आफ्नो फोन अनलक गर्न त्यसलाई फेरि सेट अप गर्नुहोस्।"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> र <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ले राम्ररी काम गरिरहेका थिएनन् र तिनलाई मेटाइयो। फिंगरप्रिन्ट प्रयोग गरी आफ्नो फोन अनलक गर्न तिनलाई फेरि सेट अप गर्नुहोस्।"</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"फेस अनलक फेरि सेटअप गर्नुहोस्"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 6ece574..0b2c507 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Je werkprofiel is niet meer beschikbaar op dit apparaat"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Te veel wachtwoordpogingen"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"De beheerder heeft het apparaat afgestaan voor persoonlijk gebruik"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Privégedeelte verwijderd"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Je organisatie staat geen privégedeelten toe op dit beheerde apparaat."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Apparaat wordt beheerd"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Dit apparaat wordt beheerd door je organisatie. Het netwerkverkeer kan worden bijgehouden. Tik voor meer informatie."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Apps hebben toegang tot je locatie"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"D-pad links"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"D-pad rechts"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"D-pad midden"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Ondertitelingsbalk van <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Hoe het werkt"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"In behandeling…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Ontgrendelen met vingerafdruk weer instellen"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> werkte niet goed en is verwijderd"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> en <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> werkten niet goed en zijn verwijderd"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> werkte niet goed en is verwijderd. Stel deze opnieuw in om de telefoon met je vingerafdruk te ontgrendelen."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> en <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> werkten niet goed en zijn verwijderd. Stel ze opnieuw in om de telefoon met je vingerafdruk te ontgrendelen."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Ontgrendelen via gezichtsherkenning weer instellen"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index ae67a74..f3cd70e 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"ଏହି ଡିଭାଇସରେ ଆପଣଙ୍କ ୱର୍କ ପ୍ରୋଫାଇଲ୍‍ ଆଉ ଉପଲବ୍ଧ ନାହିଁ"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"ବହୁତ ଥର ଭୁଲ ପାସ୍‌ୱର୍ଡ ଲେଖିଛନ୍ତି"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"ବ୍ୟକ୍ତିଗତ ବ୍ୟବହାର ପାଇଁ ଆଡ୍‌ମିନ୍ ଡିଭାଇସ୍‌କୁ ଅଲଗା କରିଛନ୍ତି"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"ପ୍ରାଇଭେଟ ସ୍ପେସ କାଢ଼ି ଦିଆଯାଇଛି"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"ଆପଣଙ୍କ ସଂସ୍ଥା ଏହି ପରିଚାଳିତ ଡିଭାଇସରେ ପ୍ରାଇଭେଟ ସ୍ପେସକୁ ଅନୁମତି ଦିଏ ନାହିଁ।"</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"ଡିଭାଇସକୁ ପରିଚାଳନା କରାଯାଉଛି"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"ଆପଣଙ୍କ ସଂସ୍ଥା ଏହି ଡିଭାଇସକୁ ପରିଚାଳନା କରନ୍ତି ଏବଂ ନେଟୱର୍କ ଟ୍ରାଫିକ୍‍ ନୀରିକ୍ଷଣ କରନ୍ତି। ବିବରଣୀ ପାଇଁ ଟାପ୍‍ କରନ୍ତୁ।"</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"ଆପଗୁଡ଼ିକ ଆପଣଙ୍କ ଲୋକେସନକୁ ଆକ୍ସେସ୍ କରିପାରିବ"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ବାମ"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad ଡାହାଣ"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad କେନ୍ଦ୍ର"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g>ର କ୍ୟାପ୍ସନ୍ ବାର୍।"</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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ଏହା କିପରି କାମ କରେ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"ବାକି ଅଛି…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ଫିଙ୍ଗରପ୍ରିଣ୍ଟ ଅନଲକ ପୁଣି ସେଟ ଅପ କରନ୍ତୁ"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> ସଠିକ ଭାବେ କାମ କରୁନାହିଁ ଏବଂ ଏହାକୁ ଡିଲିଟ କରାଯାଇଛି"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ଏବଂ <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ସଠିକ ଭାବେ କାମ କରୁନାହିଁ ଏବଂ ଏଗୁଡ଼ିକୁ ଡିଲିଟ କରାଯାଇଛି"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ସଠିକ ଭାବେ କାମ କରୁନାହିଁ ଏବଂ ଏହାକୁ ଡିଲିଟ କରାଯାଇଛି। ଟିପଚିହ୍ନ ମାଧ୍ୟମରେ ଆପଣଙ୍କ ଫୋନକୁ ଅନଲକ କରିବାକୁ ଏହାକୁ ପୁଣି ସେଟ ଅପ କରନ୍ତୁ।"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ଏବଂ <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ସଠିକ ଭାବେ କାମ କରୁନାହିଁ ଏବଂ ଏଗୁଡ଼ିକୁ ଡିଲିଟ କରାଯାଇଛି। ଆପଣଙ୍କ ଟିପଚିହ୍ନ ମାଧ୍ୟମରେ ଆପଣଙ୍କ ଫୋନକୁ ଅନଲକ କରିବାକୁ ଏଗୁଡ଼ିକୁ ପୁଣି ସେଟ ଅପ କରନ୍ତୁ।"</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"ଫେସ୍ ଅନଲକ୍ ପୁଣି ସେଟ୍ ଅପ୍ କରନ୍ତୁ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 5e79a7c..60d8737 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"ਤੁਹਾਡਾ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਹੁਣ ਇਸ ਡੀਵਾਈਸ \'ਤੇ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"ਕਈ ਵਾਰ ਗਲਤ ਪਾਸਵਰਡ ਦਾਖਲ ਕੀਤਾ ਗਿਆ"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"ਪ੍ਰਸ਼ਾਸਕ ਨੇ ਨਿੱਜੀ ਵਰਤੋਂ ਲਈ ਡੀਵਾਈਸ ਤਿਆਗਿਆ"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ ਨੂੰ ਹਟਾਇਆ ਗਿਆ"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"ਤੁਹਾਡੀ ਸੰਸਥਾ ਇਸ ਪ੍ਰਬੰਧਿਤ ਕੀਤੇ ਡੀਵਾਈਸ \'ਤੇ ਪ੍ਰਾਈਵੇਟ ਸਪੇਸਾਂ ਦੀ ਆਗਿਆ ਨਹੀਂ ਦਿੰਦੀ।"</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"ਡੀਵਾਈਸ ਪ੍ਰਬੰਧਨ ਅਧੀਨ ਹੈ"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"ਤੁਹਾਡਾ ਸੰਗਠਨ ਇਸ ਡੀਵਾਈਸ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਦਾ ਹੈ ਅਤੇ ਨੈੱਟਵਰਕ ਟਰੈਫਿਕ ਦੀ ਨਿਗਰਾਨੀ ਕਰ ਸਕਦਾ ਹੈ। ਵੇਰਵਿਆਂ ਲਈ ਟੈਪ ਕਰੋ।"</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"ਐਪਾਂ ਤੁਹਾਡੇ ਟਿਕਾਣੇ ਤੱਕ ਪਹੁੰਚ ਕਰ ਸਕਦੀਆਂ ਹਨ"</string>
@@ -835,11 +837,11 @@
     <string name="policylab_watchLogin" msgid="7599669460083719504">"ਸਕ੍ਰੀਨ ਅਣਲਾਕ ਕਰਨ ਦੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ \'ਤੇ ਨਿਗਰਾਨੀ ਰੱਖਣਾ"</string>
     <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਹੋਏ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰਨਾ ਅਤੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਵਾਰ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਜਾਣ \'ਤੇ ਟੈਬਲੈੱਟ ਨੂੰ ਲਾਕ ਕਰਨਾ ਜਾਂ ਟੈਬਲੈੱਟ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਉਣਾ।"</string>
     <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਹੋਏ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਆਪਣੇ Android TV ਡੀਵਾਈਸ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਆਪਣੇ Android TV ਡੀਵਾਈਸ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ, ਜੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
-    <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦੀ ਨਿਗਰਾਨੀ ਕਰੋ ਅਤੇ ਜੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ, ਤਾਂ ਵਾਹਨ ਆਡੀਓ ਸਿਸਟਮ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਵਾਹਨ ਆਡੀਓ ਸਿਸਟਮ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
+    <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦੀ ਨਿਗਰਾਨੀ ਕਰੋ ਅਤੇ ਜੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ, ਤਾਂ ਇੰਫ਼ੋਟੇਨਮੈਂਟ ਸਿਸਟਮ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਇੰਫ਼ੋਟੇਨਮੈਂਟ ਸਿਸਟਮ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
     <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਗਿਣਤੀ ਦੀ ਨਿਗਰਾਨੀ ਕਰੋ ਅਤੇ ਜੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ, ਤਾਂ ਫ਼ੋਨ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਫ਼ੋਨ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
     <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਹੋਏ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਟੈਬਲੈੱਟ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਟੈਬਲੈੱਟ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ, ਜੇਕਰ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
     <string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਹੋਏ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਆਪਣੇ Android TV ਡੀਵਾਈਸ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਇਸ ਵਰਤੋਂਕਾਰ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ, ਜੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
-    <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦੀ ਨਿਗਰਾਨੀ ਕਰੋ ਅਤੇ ਜੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ, ਤਾਂ ਵਾਹਨ ਆਡੀਓ ਸਿਸਟਮ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਇਸ ਪ੍ਰੋਫਾਈਲ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
+    <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦੀ ਨਿਗਰਾਨੀ ਕਰੋ ਅਤੇ ਜੇ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ, ਤਾਂ ਇੰਫ਼ੋਟੇਨਮੈਂਟ ਸਿਸਟਮ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਇਸ ਪ੍ਰੋਫਾਈਲ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
     <string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"ਸਕ੍ਰੀਨ ਨੂੰ ਅਣਲਾਕ ਕਰਦੇ ਸਮੇਂ ਟਾਈਪ ਕੀਤੇ ਗਲਤ ਪਾਸਵਰਡਾਂ ਦੀ ਸੰਖਿਆ ਦਾ ਨਿਰੀਖਣ ਕਰੋ ਅਤੇ ਫ਼ੋਨ ਨੂੰ ਲਾਕ ਕਰੋ ਜਾਂ ਫ਼ੋਨ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ ਜੇਕਰ ਬਹੁਤ ਜ਼ਿਆਦਾ ਗਲਤ ਪਾਸਵਰਡ ਟਾਈਪ ਕੀਤੇ ਹਨ।"</string>
     <string name="policylab_resetPassword" msgid="214556238645096520">"ਸਕ੍ਰੀਨ ਲਾਕ ਬਦਲੋ"</string>
     <string name="policydesc_resetPassword" msgid="4626419138439341851">"ਸਕ੍ਰੀਨ ਲਾਕ ਬਦਲੋ।"</string>
@@ -848,13 +850,13 @@
     <string name="policylab_wipeData" msgid="1359485247727537311">"ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਉਣਾ"</string>
     <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਕਰ ਕੇ ਚਿਤਾਵਨੀ ਤੋਂ ਬਿਨਾਂ ਟੈਬਲੈੱਟ ਦਾ ਡਾਟਾ ਮਿਟਾਉਣਾ।"</string>
     <string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਕਰਕੇ ਬਿਨਾਂ ਚਿਤਾਵਨੀ ਦੇ ਤੁਹਾਡੇ Android TV ਡੀਵਾਈਸ ਦਾ ਡਾਟਾ ਮਿਟਾ ਦਿੱਤਾ ਜਾਂਦਾ ਹੈ।"</string>
-    <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਕਰਕੇ ਚਿਤਾਵਨੀ ਤੋਂ ਬਿਨਾਂ ਵਾਹਨ ਆਡੀਓ ਸਿਸਟਮ ਦਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
+    <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਕਰਕੇ ਚਿਤਾਵਨੀ ਤੋਂ ਬਿਨਾਂ ਇੰਫ਼ੋਟੇਨਮੈਂਟ ਸਿਸਟਮ ਦਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
     <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"ਫੈਕਟਰੀ ਡਾਟਾ ਰੀਸੈੱਟ ਕਰ ਕੇ ਚਿਤਾਵਨੀ ਤੋਂ ਬਿਨਾਂ ਫ਼ੋਨ ਦਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
     <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"ਪ੍ਰੋਫਾਈਲ ਡਾਟਾ ਮਿਟਾਓ"</string>
     <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"ਉਪਭੋਗਤਾ  ਡਾਟਾ  ਮਿਟਾਓ"</string>
     <string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"ਬਿਨਾਂ ਚਿਤਾਵਨੀ ਦੇ ਇਸ ਟੈਬਲੈੱਟ ਤੇ ਮੌਜੂਦ ਇਸ ਵਰਤੋਂਕਾਰ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
     <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"ਬਿਨਾਂ ਚਿਤਾਵਨੀ ਦੇ ਇਸ Android TV ਡੀਵਾਈਸ \'ਤੇ ਮੌਜੂਦ ਇਸ ਵਰਤੋਂਕਾਰ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
-    <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"ਇਸ ਵਾਹਨ ਆਡੀਓ ਸਿਸਟਮ \'ਤੇ ਚਿਤਾਵਨੀ ਤੋਂ ਬਿਨਾਂ ਇਸ ਪ੍ਰੋਫਾਈਲ ਦਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
+    <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"ਇਸ ਇੰਫ਼ੋਟੇਨਮੈਂਟ ਸਿਸਟਮ \'ਤੇ ਚਿਤਾਵਨੀ ਤੋਂ ਬਿਨਾਂ ਇਸ ਪ੍ਰੋਫਾਈਲ ਦਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
     <string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"ਬਿਨਾਂ ਚਿਤਾਵਨੀ ਦੇ ਇਸ ਫ਼ੋਨ ਤੇ ਮੌਜੂਦ ਇਸ ਵਰਤੋਂਕਾਰ ਦਾ ਸਾਰਾ ਡਾਟਾ ਮਿਟਾਓ।"</string>
     <string name="policylab_setGlobalProxy" msgid="215332221188670221">"ਡੀਵਾਈਸ ਗਲੋਬਲ ਪ੍ਰੌਕਸੀ ਸੈੱਟ ਕਰੋ"</string>
     <string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"ਜਦੋਂ ਨੀਤੀ ਚਾਲੂ ਹੋਵੇ ਤਾਂ ਵਰਤੇ ਜਾਣ ਲਈ ਡੀਵਾਈਸ ਗਲੋਬਲ ਪ੍ਰੌਕਸੀ ਸੈੱਟ ਕਰੋ। ਕੇਵਲ ਡੀਵਾਈਸ ਮਾਲਕ ਗਲੋਬਲ ਪ੍ਰੌਕਸੀ ਸੈੱਟ ਕਰ ਸਕਦਾ ਹੈ।"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ਦਾ ਖੱਬੇ ਪਾਸੇ ਵਾਲਾ ਬਟਨ"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad ਦਾ ਸੱਜੇ ਪਾਸੇ ਵਾਲਾ ਬਟਨ"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad ਦਾ ਵਿਚਕਾਰਲਾ ਬਟਨ"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਦੀ ਸੁਰਖੀ ਪੱਟੀ।"</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>
@@ -2222,7 +2223,7 @@
     <string name="miniresolver_call_information" msgid="6739417525304184083">"ਤੁਹਾਡੀ ਸੰਸਥਾ ਤੁਹਾਨੂੰ ਸਿਰਫ਼ ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਤੋਂ ਕਾਲਾਂ ਕਰਨ ਦਿੰਦੀ ਹੈ"</string>
     <string name="miniresolver_sms_information" msgid="4311292661329483088">"ਤੁਹਾਡੀ ਸੰਸਥਾ ਤੁਹਾਨੂੰ ਸਿਰਫ਼ ਕੰਮ ਸੰਬੰਧੀ ਐਪਾਂ ਤੋਂ ਹੀ ਸੁਨੇਹੇ ਭੇਜਣ ਦਿੰਦੀ ਹੈ"</string>
     <string name="miniresolver_private_space_phone_information" msgid="4469511223312488570">"ਤੁਸੀਂ ਸਿਰਫ਼ ਆਪਣੀ ਨਿੱਜੀ ਫ਼ੋਨ ਐਪ ਤੋਂ ਫ਼ੋਨ ਕਾਲਾਂ ਕਰ ਸਕਦੇ ਹੋ। ਨਿੱਜੀ ਫ਼ੋਨ ਤੋਂ ਕੀਤੀਆਂ ਕਾਲਾਂ ਤੁਹਾਡੇ ਨਿੱਜੀ ਕਾਲ ਇਤਿਹਾਸ ਵਿੱਚ ਸ਼ਾਮਲ ਕੀਤੀਆਂ ਜਾਣਗੀਆਂ।"</string>
-    <string name="miniresolver_private_space_messages_information" msgid="111285656327622118">"ਤੁਸੀਂ ਸਿਰਫ਼ ਆਪਣੀ ਨਿੱਜੀ ਸੁਨੇਹਾ ਐਪ ਤੋਂ SMS ਸੁਨੇਹੇ ਭੇਜ ਸਕਦੇ ਹੋ।"</string>
+    <string name="miniresolver_private_space_messages_information" msgid="111285656327622118">"ਤੁਸੀਂ ਸਿਰਫ਼ ਆਪਣੀ ਪ੍ਰਾਈਵੇਟ ਸੁਨੇਹਾ ਐਪ ਤੋਂ SMS ਸੁਨੇਹੇ ਭੇਜ ਸਕਦੇ ਹੋ।"</string>
     <string name="miniresolver_use_personal_browser" msgid="776072682871133308">"ਨਿੱਜੀ ਬ੍ਰਾਊਜ਼ਰ ਵਰਤੋ"</string>
     <string name="miniresolver_use_work_browser" msgid="543575306251952994">"ਕੰਮ ਸੰਬੰਧੀ ਬ੍ਰਾਊਜ਼ਰ ਵਰਤੋ"</string>
     <string name="miniresolver_call" msgid="6386870060423480765">"ਕਾਲ ਕਰੋ"</string>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ਇਹ ਕਿਵੇਂ ਕੰਮ ਕਰਦਾ ਹੈ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"ਵਿਚਾਰ-ਅਧੀਨ..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਅਣਲਾਕ ਦਾ ਦੁਬਾਰਾ ਸੈੱਟਅੱਪ ਕਰੋ"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> ਚੰਗੀ ਤਰ੍ਹਾਂ ਕੰਮ ਨਹੀਂ ਕਰ ਰਿਹਾ ਸੀ ਅਤੇ ਉਸਨੂੰ ਮਿਟਾਇਆ ਗਿਆ ਸੀ"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ਅਤੇ <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ਚੰਗੀ ਤਰ੍ਹਾਂ ਕੰਮ ਨਹੀਂ ਕਰ ਰਹੇ ਸਨ ਅਤੇ ਉਨ੍ਹਾਂ ਨੂੰ ਮਿਟਾਇਆ ਗਿਆ ਸੀ"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ਚੰਗੀ ਤਰ੍ਹਾਂ ਕੰਮ ਨਹੀਂ ਕਰ ਰਿਹਾ ਸੀ ਅਤੇ ਉਸਨੂੰ ਮਿਟਾਇਆ ਗਿਆ ਸੀ। ਆਪਣੇ ਫ਼ੋਨ ਨੂੰ ਫਿੰਗਰਪ੍ਰਿੰਟ ਨਾਲ ਅਣਲਾਕ ਕਰਨ ਲਈ ਇਸਦਾ ਦੁਬਾਰਾ ਸੈੱਟਅੱਪ ਕਰੋ।"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ਅਤੇ <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ਚੰਗੀ ਤਰ੍ਹਾਂ ਕੰਮ ਨਹੀਂ ਕਰ ਰਹੇ ਸੀ ਅਤੇ ਉਨ੍ਹਾਂ ਨੂੰ ਮਿਟਾਇਆ ਗਿਆ ਸੀ। ਆਪਣੇ ਫ਼ੋਨ ਨੂੰ ਆਪਣੇ ਫਿੰਗਰਪ੍ਰਿੰਟ ਨਾਲ ਅਣਲਾਕ ਕਰਨ ਲਈ ਇਨ੍ਹਾਂ ਦਾ ਦੁਬਾਰਾ ਸੈੱਟਅੱਪ ਕਰੋ।"</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"ਫ਼ੇਸ ਅਣਲਾਕ ਦਾ ਦੁਬਾਰਾ ਸੈੱਟਅੱਪ ਕਰੋ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index d810710..ec5fee7 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -203,6 +203,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Twój profil służbowy nie jest już dostępny na tym urządzeniu"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Zbyt wiele prób podania hasła"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administrator odstąpił urządzenie do użytku osobistego"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Przestrzeń prywatna została usunięta"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Twoja organizacja nie zezwala na przestrzenie prywatne na tym urządzeniu zarządzanym."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Urządzenie jest zarządzane"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Twoja organizacja zarządza tym urządzeniem i może monitorować ruch w sieci. Kliknij, by dowiedzieć się więcej."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikacje mogą mieć dostęp do Twojej lokalizacji"</string>
@@ -2196,7 +2198,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad – w lewo"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad – w prawo"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad – środek"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Pasek napisów w aplikacji <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <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>
@@ -2417,10 +2418,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Jak to działa"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Oczekiwanie…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Skonfiguruj ponownie odblokowywanie odciskiem palca"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"Odcisk palca <xliff:g id="FINGERPRINT">%s</xliff:g> nie sprawdzał się dobrze i został usunięty"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"Odciski palców <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nie sprawdzały się dobrze i zostały usunięte"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Odcisk palca <xliff:g id="FINGERPRINT">%s</xliff:g> nie sprawdzał się dobrze i został usunięty. Skonfiguruj go ponownie, aby odblokowywać telefon odciskiem palca."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Odciski palca <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> i <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nie sprawdzały się dobrze i zostały usunięte. Skonfiguruj je ponownie, aby odblokowywać telefon odciskiem palca."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Skonfiguruj ponownie rozpoznawanie twarzy"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index e3b49a0..7067a89 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -202,6 +202,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Seu perfil de trabalho não está mais disponível neste dispositivo"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Muitas tentativas de senha"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"O administrador renunciou ao dispositivo para uso pessoal"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Espaço privado removido"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Sua organização não permite espaços privados neste dispositivo gerenciado."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"O dispositivo é gerenciado"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Sua organização gerencia este dispositivo e pode monitorar o tráfego de rede. Toque para ver detalhes."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Os apps podem acessar seu local"</string>
@@ -284,8 +286,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Ajuda de voz"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Bloqueio total"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"&gt;999"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Responder"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Nova notificação"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Teclado físico"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Segurança"</string>
@@ -2196,7 +2197,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Botão direcional: para a esquerda"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Botão direcional: para a direita"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Botão direcional: centro"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Barra de legendas do app <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
@@ -2417,10 +2417,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Como funciona"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configurar o Desbloqueio por impressão digital de novo"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"A impressão digital <xliff:g id="FINGERPRINT">%s</xliff:g> não estava funcionando bem e foi excluída"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"As impressões digitais <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> não estavam funcionando bem e foram excluídas"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"A impressão digital <xliff:g id="FINGERPRINT">%s</xliff:g> não estava funcionando bem e foi excluída. Configure de novo para desbloquear o smartphone com a impressão digital."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"As impressões digitais <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> não estavam funcionando bem e foram excluídas. Configure de novo para desbloquear o smartphone com a impressão digital."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Configure o Desbloqueio facial de novo"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 512f697..6632f85 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -202,6 +202,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"O seu perfil de trabalho já não está disponível neste dispositivo"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Demasiadas tentativas de introdução da palavra-passe"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"O administrador anulou o dispositivo para utilização pessoal."</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Espaço privado removido"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"A sua organização não permite espaços privados neste dispositivo gerido."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"O dispositivo é gerido"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"A sua entidade gere este dispositivo e pode monitorizar o tráfego de rede. Toque para obter mais detalhes."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"As apps podem aceder à sua localização"</string>
@@ -2195,7 +2197,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Teclado direcional: para a esquerda"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Teclado direcional: para a direita"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Teclado direcional: centrar"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Barra de legendas da app <xliff:g id="APP_NAME">%1$s</xliff:g>."</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>
@@ -2416,10 +2417,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Como funciona"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configure o Desbloqueio por impressão digital novamente"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"A <xliff:g id="FINGERPRINT">%s</xliff:g> não estava a funcionar bem e foi eliminada"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"A <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e a <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> não estavam a funcionar bem e foram eliminadas"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"A <xliff:g id="FINGERPRINT">%s</xliff:g> não estava a funcionar bem e foi eliminada. Configure-a novamente para desbloquear o telemóvel com a impressão digital."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"A <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e a <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> não estavam a funcionar bem e foram eliminadas. Configure-as novamente para desbloquear o telemóvel com a sua impressão digital."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Configure o Desbloqueio facial novamente"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index e3b49a0..7067a89 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -202,6 +202,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Seu perfil de trabalho não está mais disponível neste dispositivo"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Muitas tentativas de senha"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"O administrador renunciou ao dispositivo para uso pessoal"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Espaço privado removido"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Sua organização não permite espaços privados neste dispositivo gerenciado."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"O dispositivo é gerenciado"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Sua organização gerencia este dispositivo e pode monitorar o tráfego de rede. Toque para ver detalhes."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Os apps podem acessar seu local"</string>
@@ -284,8 +286,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Ajuda de voz"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Bloqueio total"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"&gt;999"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Responder"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Nova notificação"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Teclado físico"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Segurança"</string>
@@ -2196,7 +2197,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Botão direcional: para a esquerda"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Botão direcional: para a direita"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Botão direcional: centro"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Barra de legendas do app <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
@@ -2417,10 +2417,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Como funciona"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Pendente…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configurar o Desbloqueio por impressão digital de novo"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"A impressão digital <xliff:g id="FINGERPRINT">%s</xliff:g> não estava funcionando bem e foi excluída"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"As impressões digitais <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> não estavam funcionando bem e foram excluídas"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"A impressão digital <xliff:g id="FINGERPRINT">%s</xliff:g> não estava funcionando bem e foi excluída. Configure de novo para desbloquear o smartphone com a impressão digital."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"As impressões digitais <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> e <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> não estavam funcionando bem e foram excluídas. Configure de novo para desbloquear o smartphone com a impressão digital."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Configure o Desbloqueio facial de novo"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 7de9952..bf82cd8c 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -202,6 +202,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Profilul de serviciu nu mai este disponibil pe acest dispozitiv"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Prea multe încercări de introducere a parolei"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administratorul a retras dispozitivul pentru uz personal"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Spațiul privat a fost eliminat"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Organizația ta nu permite spațiile private pe acest dispozitiv gestionat."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Dispozitivul este gestionat"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Organizația ta gestionează acest dispozitiv și poate monitoriza traficul în rețea. Atinge pentru mai multe detalii."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplicațiile îți pot accesa locația"</string>
@@ -284,8 +286,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Asistent vocal"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Blocare strictă"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"˃999"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Răspunde"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Notificare nouă"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Tastatură fizică"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Securitate"</string>
@@ -2196,7 +2197,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad stânga"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad dreapta"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad centru"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Bară cu legenda pentru <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
@@ -2417,10 +2417,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cum funcționează"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"În așteptare..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Configurează din nou Deblocarea cu amprenta"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> nu funcționa bine și a fost ștearsă"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> și <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nu funcționau bine și au fost șterse"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> nu funcționa bine și s-a șters. Configureaz-o din nou pentru a-ți debloca telefonul cu amprenta."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> și <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nu funcționau bine și s-au șters. Configurează-le din nou pentru a-ți debloca telefonul cu amprenta."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Reconfigurează Deblocarea facială"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index d999bf5..c1900a8 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -203,6 +203,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Ваш рабочий профиль больше не доступен на этом устройстве"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Слишком много попыток ввести пароль."</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Администратор освободил устройство для личного использования"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Частное пространство удалено"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Ваша организация запрещает использовать частное пространство на этом управляемом устройстве."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Это управляемое устройство"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Ваша организация управляет этим устройством и может отслеживать сетевой трафик. Подробнее…"</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"У приложений есть доступ к вашим геоданным"</string>
@@ -285,8 +287,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Аудиоподсказки"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Блокировка входа"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"&gt;999"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Ответить"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Новое уведомление"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Физическая клавиатура"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Безопасность"</string>
@@ -2197,14 +2198,13 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"D-pad – влево"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"D-pad – вправо"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"D-pad – по центру"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Строка субтитров в приложении \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"."</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>
     <string name="conversation_title_fallback_one_to_one" msgid="1980753619726908614">"Чат"</string>
     <string name="conversation_title_fallback_group_chat" msgid="456073374993104303">"Групповой чат"</string>
     <string name="unread_convo_overflow" msgid="920517615597353833">"<xliff:g id="MAX_UNREAD_COUNT">%1$d</xliff:g>+"</string>
-    <string name="resolver_personal_tab" msgid="2051260504014442073">"Личное"</string>
+    <string name="resolver_personal_tab" msgid="2051260504014442073">"Личный"</string>
     <string name="resolver_work_tab" msgid="2690019516263167035">"Рабочее"</string>
     <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"Просмотр личных данных"</string>
     <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"Просмотр рабочих данных"</string>
@@ -2418,10 +2418,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Узнать принцип работы"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Обработка…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Настройте разблокировку по отпечатку пальца заново"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"Отпечаток пальца \"<xliff:g id="FINGERPRINT">%s</xliff:g>\" оказался неудачным и был удален."</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"Отпечатки пальцев \"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>\" и \"<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>\" оказались неудачными и были удалены."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Отпечаток пальца \"<xliff:g id="FINGERPRINT">%s</xliff:g>\" оказался неудачным и был удален. Чтобы использовать разблокировку с помощью отпечатка пальца, настройте ее заново."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Отпечатки пальцев \"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>\" и \"<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>\" оказались неудачными и были удалены. Чтобы использовать разблокировку с помощью отпечатка пальца, настройте ее заново."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Настройте фейсконтроль заново"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index a8eda4f..f216ce2 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"ඔබේ කාර්යාල පැතිකඩ මෙම උපාංගය මත තවදුරටත් ලබා ගැනීමට නොහැකිය"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"මුරපද උත්සාහ කිරීම් ඉතා වැඩි ගණනකි"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"පරිපාලක පුද්ගලික භාවිතය සඳහා උපාංගය අත්හැර දමන ලදී"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"පුද්ගලික ඉඩ ඉවත් කරන ලදි"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"ඔබේ සංවිධානය මෙම කළමනා කෙරෙන උපාංගය මත පුද්ගලික ඉඩවලට ඉඩ නොදෙයි."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"උපාංගය කළමනාකරණය කෙරේ"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"ඔබගේ ආයතනය මෙම උපාංගය කළමනාකරණය කරන අතර එය ජාල තදබදය නිරීක්ෂණය කළ හැක. විස්තර සඳහා තට්ටු කරන්න."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"යෙදුම්වලට ඔබේ ස්ථානයට ප්‍රවේශ විය හැකිය"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"හඬ සහායක"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"අගුලු දැමීම"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"පිළිතුර"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"නව දැනුම්දීම"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"භෞතික යතුරු පුවරුව"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"ආරක්ෂාව"</string>
@@ -2195,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad වම"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad දකුණ"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad මැද"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> හි සිරස්තල තීරුව."</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>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"එය ක්‍රියා කරන ආකාරය"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"පොරොත්තුයි..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ඇඟිලි සලකුණු අගුලු හැරීම නැවත සකසන්න"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> හොඳින් ක්‍රියා නොකළ අතර එය මකන ලදි"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> සහ <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> හොඳින් ක්‍රියා නොකළ අතර ඒවා මකන ලදි"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> හොඳින් ක්‍රියා නොකළේය, එය මකන ලදි ඇඟිලි සලකුණ මගින් ඔබේ දුරකථනය අගුලු හැරීමට එය නැවත සකසන්න."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> සහ <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> හොඳින් ක්‍රියා නොකළේය, කාර්යසාධනය දියුණූ කිරීමට ඒවා මකන ලදි. ඔබේ ඇඟිලි සලකුණ මගින් ඔබේ දුරකථනය අගුලු හැරීමට ඒවා නැවත සකසන්න."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"මුහුණෙන් අගුලු හැරීම නැවත සකසන්න"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 46897cb..9c2a875 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -203,6 +203,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Váš pracovný profil už v tomto zariadení nie je k dispozícii"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Príliš veľa pokusov o zadanie hesla"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Správca uvoľnil toto zariadenie na osobné používanie"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Súkromný priestor bol odstránený"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Vaša organizácia nepovoľuje súkromné priestory v tomto spravovanom zariadení."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Zariadenie je spravované"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Vaša organizácia spravuje toto zariadenie a môže sledovať sieťovú premávku. Klepnutím zobrazíte podrobnosti."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikácie majú prístup k vašej polohe"</string>
@@ -285,8 +287,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Hlasový asistent"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Uzamknúť"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Odpovedať"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Nové upozornenie"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fyzická klávesnica"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Zabezpečenie"</string>
@@ -1191,8 +1192,8 @@
     <string name="deleteText" msgid="4200807474529938112">"Odstrániť"</string>
     <string name="inputMethod" msgid="1784759500516314751">"Metóda vstupu"</string>
     <string name="editTextMenuTitle" msgid="857666911134482176">"Operácie s textom"</string>
-    <string name="error_handwriting_unsupported" msgid="7809438534946014050">"Ručné písanie nie je v tomto poli podporované"</string>
-    <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"V poliach pre heslá nie je ručné písanie podporované"</string>
+    <string name="error_handwriting_unsupported" msgid="7809438534946014050">"V tomto poli nie je rukopis podporovaný"</string>
+    <string name="error_handwriting_unsupported_password" msgid="5095401146106891087">"V poliach pre heslá nie je rukopis podporovaný"</string>
     <string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Späť"</string>
     <string name="input_method_ime_switch_button_desc" msgid="2736542240252198501">"Prepnúť metódu vstupu"</string>
     <string name="low_internal_storage_view_title" msgid="9024241779284783414">"Nedostatok ukladacieho priestoru"</string>
@@ -2197,7 +2198,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Stlačiť tlačidlo doľava krížového ovládača"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Stlačiť tlačidlo doprava krížového ovládača"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Stlačiť stredné tlačidlo krížového ovládača"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Popis aplikácie <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
@@ -2398,9 +2398,9 @@
     <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Rozloženie klávesnice je nastavené na jazyky <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g> a <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Môžete to zmeniť klepnutím."</string>
     <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Fyzické klávesnice sú nakonfigurované"</string>
     <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Klávesnice si zobrazíte klepnutím"</string>
-    <string name="profile_label_private" msgid="6463418670715290696">"Súkromný"</string>
+    <string name="profile_label_private" msgid="6463418670715290696">"Súkromné"</string>
     <string name="profile_label_clone" msgid="769106052210954285">"Klon"</string>
-    <string name="profile_label_work" msgid="3495359133038584618">"Pracovný"</string>
+    <string name="profile_label_work" msgid="3495359133038584618">"Pracovné"</string>
     <string name="profile_label_work_2" msgid="4691533661598632135">"2. pracovný"</string>
     <string name="profile_label_work_3" msgid="4834572253956798917">"3. pracovný"</string>
     <string name="profile_label_test" msgid="9168641926186071947">"Testovací"</string>
@@ -2418,10 +2418,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ako to funguje"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Nespracovaná…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Znova nastavte odomknutie odtlačkom prsta"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"Odtlačok <xliff:g id="FINGERPRINT">%s</xliff:g> nefungoval správne a bol odstránený"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"Odtlačky <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> a <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nefungovali správne a boli odstránené"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Odtlačok <xliff:g id="FINGERPRINT">%s</xliff:g> nefungoval správne a bol odstránený. Ak chcete odomykať telefón odtlačkom prsta, nastavte ho znova."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Odtlačky <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> a <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nefungovali správne a boli odstránené. Ak chcete odomykať telefón odtlačkom prsta, nastavte ich znova."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Znova nastavte odomknutie tvárou"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 5c3696a..b3530e7 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -203,6 +203,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Vaš delovni profil ni več na voljo v tej napravi"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Preveč poskusov vnosa gesla"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Skrbnik je napravo prepustil osebni uporabi"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Zasebni prostor odstranjen"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Vaša organizacija ne dovoli zasebnih prostorov v tej upravljani napravi."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Naprava je upravljana"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Vaša organizacija upravlja to napravo in lahko nadzira omrežni promet. Dotaknite se za podrobnosti."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikacije imajo dostop do vaše lokacije"</string>
@@ -2196,7 +2198,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Smerni gumb levo"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Smerni gumb desno"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Smerni gumb sredina"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Vrstica s podnapisi aplikacije <xliff:g id="APP_NAME">%1$s</xliff:g>."</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>
@@ -2417,10 +2418,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Kako deluje"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"V teku …"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Vnovična nastavitev odklepanja s prstnim odtisom"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> ni deloval pravilno in je bil izbrisan"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> in <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nista delovala pravilno in sta bila izbrisana"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> ni deloval pravilno in je bil izbrisan. Znova ga nastavite, če želite telefon odklepati s prstnim odtisom."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> in <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nista delovala pravilno in sta bila izbrisana. Znova ju nastavite, če želite telefon odklepati s prstnim odtisom."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Vnovična nastavitev odklepanja z obrazom"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 4a6bdcb..5f9a642 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -201,6 +201,10 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Profili yt i punës nuk është më i disponueshëm në këtë pajisje"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Shumë përpjekje për fjalëkalimin"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administratori e refuzoi pajisjen për përdorim personal"</string>
+    <!-- no translation found for private_space_deleted_by_admin (1484365588862066939) -->
+    <skip />
+    <!-- no translation found for private_space_deleted_by_admin_details (7007781735201818689) -->
+    <skip />
     <string name="network_logging_notification_title" msgid="554983187553845004">"Pajisja është e menaxhuar"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Organizata jote e menaxhon këtë pajisje dhe mund të monitorojë trafikun e rrjetit. Trokit për detaje."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Aplikacionet mund të kenë qasje te vendndodhja jote"</string>
@@ -283,8 +287,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Ndihma zanore"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Blloko"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Përgjigju"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Njoftim i ri"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Tastiera fizike"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Siguria"</string>
@@ -2195,7 +2198,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Majtas në bllokun e drejtimit"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Djathtas në bllokun e drejtimit"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Qendra e bllokut të drejtimit"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Shiriti i nëntitullit të <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
@@ -2396,7 +2398,7 @@
     <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Struktura e tastierës u caktua në: <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g>… Trokit për ta ndryshuar."</string>
     <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Tastierat fizike u konfiguruan"</string>
     <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Trokit për të parë tastierat"</string>
-    <string name="profile_label_private" msgid="6463418670715290696">"Privat"</string>
+    <string name="profile_label_private" msgid="6463418670715290696">"Private"</string>
     <string name="profile_label_clone" msgid="769106052210954285">"Klon"</string>
     <string name="profile_label_work" msgid="3495359133038584618">"Puna"</string>
     <string name="profile_label_work_2" msgid="4691533661598632135">"Puna 2"</string>
@@ -2416,10 +2418,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Si funksionon"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Në pritje..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Konfiguro përsëri \"Shkyçjen me gjurmën e gishtit\""</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> nuk po funksiononte mirë dhe u fshi"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> dhe <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nuk po funksiononin mirë dhe u fshinë"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> nuk po funksiononte mirë dhe u fshi. Konfiguroje përsëri për ta shkyçur telefonin tënd me gjurmën e gishtit."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> dhe <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> nuk po funksiononin mirë dhe u fshinë. Konfiguroji përsëri për ta shkyçur telefonin tënd me gjurmën e gishtit."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Konfiguro \"Shkyçjen me fytyrë\" përsëri"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 86387c9..301fe24 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -202,6 +202,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Пословни профил више није доступан на овом уређају"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Превише покушаја уноса лозинке"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Администратор је уступио уређај за личну употребу"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Приватан простор је уклоњен"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Организација не дозвољава приватне просторе на овом управљаном уређају."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Уређајем се управља"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Организација управља овим уређајем и може да надгледа мрежни саобраћај. Додирните за детаље."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Апликације могу да приступају вашој локацији"</string>
@@ -284,8 +286,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Гласовна помоћ"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Закључавање"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Одговори"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Ново обавештење"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Физичка тастатура"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Безбедност"</string>
@@ -2196,7 +2197,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"налево на D-pad-у"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"надесно на D-pad-у"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"центар на D-pad-у"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Трака са насловима апликације <xliff:g id="APP_NAME">%1$s</xliff:g>."</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>
@@ -2417,10 +2417,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Принцип рада"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"На чекању..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Поново подесите откључавање отиском прста"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> није функционисао и избрисали смо га"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> и <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> нису функционисали и избрисали смо их"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> није функционисао и избрисали смо га. Поново га подесите да бисте телефон откључавали отиском прста."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> и <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> нису функционисали и избрисали смо их. Поново их подесите да бисте телефон откључавали отиском прста."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Поново подесите откључавање лицем"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 6c15ad8..b3c2063 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Jobbprofilen är inte längre tillgänglig på enheten"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"För många försök med lösenord"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administratören tillåter inte längre privat bruk av enheten"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Privat område har tagits bort"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Din organisation tillåter inte privata områden på den här hanterade enheten."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Enheten hanteras"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Organisationen hanterar den här enheten och kan övervaka nätverkstrafiken. Tryck om du vill veta mer."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Appar har åtkomst till din plats"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Voice Assist"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Låsning"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Svara"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Ny avisering"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fysiskt tangentbord"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Säkerhet"</string>
@@ -2195,14 +2196,13 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Styrkors, vänster"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Styrkors, höger"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Styrkors, mitten"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Textningsfält för <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
     <string name="conversation_title_fallback_one_to_one" msgid="1980753619726908614">"Konversation"</string>
     <string name="conversation_title_fallback_group_chat" msgid="456073374993104303">"Gruppkonversation"</string>
     <string name="unread_convo_overflow" msgid="920517615597353833">"<xliff:g id="MAX_UNREAD_COUNT">%1$d</xliff:g>+"</string>
-    <string name="resolver_personal_tab" msgid="2051260504014442073">"Privat"</string>
+    <string name="resolver_personal_tab" msgid="2051260504014442073">"Personlig"</string>
     <string name="resolver_work_tab" msgid="2690019516263167035">"Jobb"</string>
     <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"Personlig vy"</string>
     <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"Jobbvy"</string>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Så fungerar det"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Väntar …"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Konfigurera fingeravtryckslås igen"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> fungerade inte bra och har raderats"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> och <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> fungerade inte bra och har raderats"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> fungerade inte bra och har raderats. Konfigurera det igen för att låsa upp telefonen med fingeravtryck."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> och <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> fungerade inte bra och har raderats. Konfigurera dem igen för att låsa upp telefonen med fingeravtryck."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Konfigurera ansiktslås igen"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index e8ba087..f15796e 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Wasifu wako wa kazini haupatikani tena kwenye kifaa hiki"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Umejaribu kuweka nenosiri mara nyingi mno"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Msimamizi aliacha kutumia kifaa kwa matumizi ya binafsi"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Sehemu ya faragha imeondolewa"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Shirika lako haliruhusu sehemu za faragha kwenye kifaa hiki kinachodhibitiwa."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Kifaa kinadhibitiwa"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Shirika lako linadhibiti kifaa hiki na huenda likafuatilia shughuli kwenye mtandao. Gusa ili upate maelezo zaidi."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Programu zinaweza kutambua mahali ulipo"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Kitufe cha kushoto cha Dpad"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Kitufe cha kulia cha Dpad"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Kitufe cha katikati cha Dpad"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Upau wa manukuu wa <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Utaratibu wake"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Inashughulikiwa..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Weka tena mipangilio ya Kufungua kwa Alama ya Kidole"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"Alama ya <xliff:g id="FINGERPRINT">%s</xliff:g> ilikuwa na hitilafu na imefutwa"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"Alama za <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> na <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> zilikuwa na hitilafu na zimefutwa"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Alama ya <xliff:g id="FINGERPRINT">%s</xliff:g> ilikuwa na hitilafu na imefutwa. Iweke tena ili ufungue simu yako kwa alama ya kidole."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Alama za <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> na <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> zilikuwa na hitilafu na zimefutwa. Ziweke tena ili ufungue simu yako kwa alama ya kidole."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Weka tena mipangilio ya Kufungua kwa Uso"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 231b14c..f615965 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"இந்தச் சாதனத்தில் இனி பணிக் கணக்கு கிடைக்காது"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"கடவுச்சொல்லை அதிக முறை தவறாக முயற்சித்துவிட்டீர்கள்"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"நிர்வாகியால் தனிப்பட்ட உபயோகத்திற்காக ஒதுக்கப்பட்ட சாதனம்"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"ரகசிய இடம் அகற்றப்பட்டது"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"இந்த நிர்வகிக்கப்படும் சாதனத்தில் ரகசிய இடங்களை உங்கள் நிறுவனம் அனுமதிப்பதில்லை."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"சாதனம் நிர்வகிக்கப்படுகிறது"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"உங்கள் நிறுவனம் இந்தச் சாதனத்தை நிர்வகிக்கும், அத்துடன் அது நெட்வொர்க் ட்ராஃபிக்கைக் கண்காணிக்கலாம். விவரங்களுக்கு, தட்டவும்."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"ஆப்ஸ் உங்கள் இருப்பிடத்தை அணுக முடியும்"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"குரல் உதவி"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"பூட்டு"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"பதிலளி"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"புதிய அறிவிப்பு"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"கைமுறை கீபோர்டு"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"பாதுகாப்பு"</string>
@@ -2195,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"இடது திசை காட்டும் பட்டன்"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"வலது திசை காட்டும் பட்டன்"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"மையப் பகுதியைக் காட்டும் பட்டன்"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸின் தலைப்புப் பட்டி."</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>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 5517dbf..a84f6eb 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"ఈ పరికరంలో మీ కార్యాలయ ప్రొఫైల్ ఇప్పుడు అందుబాటులో లేదు"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"చాలా ఎక్కువ పాస్‌వర్డ్ ప్రయత్నాలు చేశారు"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"వ్యక్తిగత వినియోగం కోసం నిర్వాహకులు పరికరాన్ని తీసి వేశారు"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"ప్రైవేట్ స్పేస్ తీసివేయబడింది"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"మీ సంస్థ ఈ మేనేజ్ చేసే పరికరంలో ప్రైవేట్ స్పేస్‌లను అనుమతించదు."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"పరికరం నిర్వహించబడింది"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"మీ సంస్థ ఈ పరికరాన్ని నిర్వహిస్తుంది మరియు నెట్‌వర్క్ ట్రాఫిక్‌ని పర్యవేక్షించవచ్చు. వివరాల కోసం నొక్కండి."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"యాప్‌లు మీ లొకేషన్‌ను యాక్సెస్ చేయగలవు"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ఎడమవైపున"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad కుడివైపున"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"DPad మధ్యన"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> క్యాప్షన్ బార్."</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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"ఇది ఎలా పని చేస్తుంది"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"పెండింగ్‌లో ఉంది..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"వేలిముద్ర అన్‌లాక్‌ను మళ్లీ సెటప్ చేయండి"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> సరిగ్గా పని చేయడం లేదు, తొలగించబడింది"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>, <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> బాగా పని చేయడం లేదు, తొలగించబడ్డాయి"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> సరిగ్గా పని చేయడం లేదు, తొలగించబడింది. వేలిముద్రతో మీ ఫోన్‌ను అన్‌లాక్ చేయడానికి దాన్ని మళ్లీ సెటప్ చేయండి."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>, <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> బాగా పని చేయడం లేదు, తొలగించబడ్డాయి. మీ వేలిముద్రతో మీ ఫోన్‌ను అన్‌లాక్ చేయడానికి వాటిని మళ్లీ సెటప్ చేయండి."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"ఫేస్ అన్‌లాక్‌ను మళ్లీ సెటప్ చేయండి"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 070875f..e34b526 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"โปรไฟล์งานของคุณไม่สามารถใช้ในอุปกรณ์นี้อีกต่อไป"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"พยายามป้อนรหัสผ่านหลายครั้งเกินไป"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"ผู้ดูแลระบบปล่อยอุปกรณ์ให้คุณใช้งานส่วนตัว"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"นำพื้นที่ส่วนตัวออกแล้ว"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"องค์กรของคุณไม่อนุญาตให้มีพื้นที่ส่วนตัวในอุปกรณ์ที่มีการจัดการเครื่องนี้"</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"อุปกรณ์มีการจัดการ"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"องค์กรของคุณจัดการอุปกรณ์นี้และอาจตรวจสอบการจราจรของข้อมูลในเครือข่าย แตะเพื่อดูรายละเอียด"</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"แอปจะเข้าถึงตำแหน่งของคุณได้"</string>
@@ -2194,14 +2196,13 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad ซ้าย"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad ขวา"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad กึ่งกลาง"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"แถบคำบรรยาย <xliff:g id="APP_NAME">%1$s</xliff:g>"</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>
     <string name="conversation_title_fallback_one_to_one" msgid="1980753619726908614">"การสนทนา"</string>
     <string name="conversation_title_fallback_group_chat" msgid="456073374993104303">"บทสนทนากลุ่ม"</string>
     <string name="unread_convo_overflow" msgid="920517615597353833">"<xliff:g id="MAX_UNREAD_COUNT">%1$d</xliff:g>+"</string>
-    <string name="resolver_personal_tab" msgid="2051260504014442073">"ส่วนตัว"</string>
+    <string name="resolver_personal_tab" msgid="2051260504014442073">"ส่วนบุคคล"</string>
     <string name="resolver_work_tab" msgid="2690019516263167035">"งาน"</string>
     <string name="resolver_personal_tab_accessibility" msgid="5739524949153091224">"มุมมองส่วนตัว"</string>
     <string name="resolver_work_tab_accessibility" msgid="4753168230363802734">"ดูงาน"</string>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"วิธีการทำงาน"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"รอดำเนินการ..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"ตั้งค่าการปลดล็อกด้วยลายนิ้วมืออีกครั้ง"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> ทำงานได้ไม่ดีและถูกลบออกไปแล้ว"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> และ <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ทำงานได้ไม่ดีและถูกลบออกไปแล้ว"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g>ทำงานได้ไม่ดีและถูกลบออกไปแล้ว ตั้งค่าอีกครั้งเพื่อปลดล็อกโทรศัพท์ด้วยลายนิ้วมือ"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> และ <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ทำงานได้ไม่ดีและถูกลบออกไปแล้ว ตั้งค่าอีกครั้งเพื่อปลดล็อกโทรศัพท์ด้วยลายนิ้วมือ"</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"ตั้งค่าการปลดล็อกด้วยใบหน้าอีกครั้ง"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 45a66a3..4288460 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Hindi na available sa device na ito ang iyong profile sa trabaho"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Masyadong maraming pagsubok sa password"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Inalis ng admin ang device para sa personal na paggamit"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Naalis ang pribadong space"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Hindi pinapayagan ng iyong organisasyon ang mga pribadong space sa pinapamahalaang device na ito."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Pinamamahalaan ang device"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Pinamamahalaan ng iyong organisasyon ang device na ito, at maaari nitong subaybayan ang trapiko sa network. I-tap para sa mga detalye."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Maa-access ng mga app ang iyong lokasyon"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad Left"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad Right"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad Center"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Caption bar ng <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Paano ito gumagana"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Nakabinbin..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"I-set up ulit ang Pag-unlock Gamit ang Fingerprint"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"Hindi gumagana nang maayos ang <xliff:g id="FINGERPRINT">%s</xliff:g> at na-delete ito"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"Hindi gumagana nang maayos ang <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> at <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> at na-delete ang mga ito"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Hindi gumagana nang maayos ang <xliff:g id="FINGERPRINT">%s</xliff:g> at na-delete na ito. I-set up ulit ito para ma-unlock ang iyong telepono sa pamamagitan ng fingerprint."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Hindi gumagana nang maayos ang <xliff:g id="FINGERPRINT_0">%1$s</xliff:g> at <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> at na-delete na ang mga ito. I-set up ulit ang mga ito para ma-unlock ang iyong telepono gamit ang fingerprint mo."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"I-set up ulit ang Pag-unlock Gamit ang Mukha"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 64498b5..906ccbc 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"İş profiliniz arık bu cihazda kullanılamıyor"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Çok fazla şifre denemesi yapıldı"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Yönetici, cihazı kişisel kullanım için serbest bıraktı"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Özel alan kaldırıldı"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Kuruluşunuz bu yönetilen cihazda özel alan kullanılmasına izin vermiyor."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Cihaz yönetiliyor"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Kuruluşunuz bu cihazı yönetmekte olup ağ trafiğini izleyebilir. Ayrıntılar için dokunun."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Uygulamalar konumunuza erişebilir"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Sesli Yardım"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Tam kilitleme"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Yanıtla"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Yeni bildirim"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Fiziksel klavye"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Güvenlik"</string>
@@ -2195,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad Sol"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad Sağ"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad Orta"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulamasının başlık çubuğu."</string>
     <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>
@@ -2396,7 +2396,7 @@
     <string name="keyboard_layout_notification_more_than_three_selected_message" msgid="1581834181578206937">"Klavye düzeni <xliff:g id="LAYOUT_1">%1$s</xliff:g>, <xliff:g id="LAYOUT_2">%2$s</xliff:g>, <xliff:g id="LAYOUT_3">%3$s</xliff:g> olarak ayarlandı… Değiştirmek için dokunun."</string>
     <string name="keyboard_layout_notification_multiple_selected_title" msgid="5242444914367024499">"Fiziksel klavyeler yapılandırıldı"</string>
     <string name="keyboard_layout_notification_multiple_selected_message" msgid="6576533454124419202">"Klavyeleri görüntülemek için dokunun"</string>
-    <string name="profile_label_private" msgid="6463418670715290696">"Gizli"</string>
+    <string name="profile_label_private" msgid="6463418670715290696">"Özel"</string>
     <string name="profile_label_clone" msgid="769106052210954285">"Klon"</string>
     <string name="profile_label_work" msgid="3495359133038584618">"İş"</string>
     <string name="profile_label_work_2" msgid="4691533661598632135">"İş 2"</string>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"İşleyiş şekli"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Bekliyor..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Parmak İzi Kilidi\'ni tekrar kurun"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> iyi çalışmadığı için silindi"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ve <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> iyi çalışmadığı için silindi"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> iyi çalışmadığı için silindi. Telefonunuzun kilidini parmak iziyle açmak için tekrar kurun."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> ve <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> iyi çalışmadığı için silindi. Telefonunuzun kilidini parmak izinizle açmak için tekrar kurun."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Yüz Tanıma Kilidi\'ni tekrar kurun"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index fcea7bb..cc9df2e 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -203,6 +203,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Робочий профіль більше не доступний на цьому пристрої"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Забагато спроб ввести пароль"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Адміністратор не дозволив використовувати пристрій для особистих потреб"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Приватний простір видалено"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Ваша організація не дозволяє мати приватні простори на цьому керованому пристрої."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Пристрій контролюється"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Адміністратор вашої організації контролює цей пристрій і відстежує мережевий трафік. Торкніться, щоб дізнатися більше."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Додаток має доступ до геоданих"</string>
@@ -285,8 +287,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Голос. підказки"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Блокування"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Відповісти"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Нове сповіщення"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Фізична клавіатура"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Безпека"</string>
@@ -2197,7 +2198,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Кнопка \"вліво\" панелі керування"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Кнопка \"вправо\" панелі керування"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Центральна кнопка панелі керування"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Смуга із субтитрами для додатка <xliff:g id="APP_NAME">%1$s</xliff:g>."</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>
@@ -2418,10 +2418,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Як це працює"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Обробка…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Налаштуйте розблокування відбитком пальця повторно"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"Відбиток \"<xliff:g id="FINGERPRINT">%s</xliff:g>\" працював неналежним чином, і його видалено"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"Відбитки \"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>\" і \"<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>\" працювали неналежним чином, і їх видалено"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"Відбиток \"<xliff:g id="FINGERPRINT">%s</xliff:g>\" працював неналежним чином, і його видалено. Налаштуйте його ще раз, щоб розблоковувати телефон за допомогою відбитка пальця."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"Відбитки \"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>\" і \"<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>\" працювали неналежним чином, і їх видалено. Налаштуйте їх ще раз, щоб розблоковувати телефон за допомогою відбитка пальця."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Налаштуйте фейс-контроль повторно"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index da592a4..c0be641 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"آپ کا دفتری پروفائل اس آلہ پر مزید دستیاب نہیں ہے"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"پاس ورڈ کی بہت ساری کوششیں"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"منتظم نے ذاتی استعمال کے لیے آلہ کو دستبردار کیا ہے"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"پرائیویٹ اسپیس کو ہٹا دیا گیا"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"آپ کی تنظیم اس زیر انتظام آلے پر پرائیویٹ اسپیسز کو اجازت نہیں دیتی ہے۔"</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"آلہ زیر انتظام ہے"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"آپ کی تنظیم اس آلے کا نظم کرتی ہے اور وہ نیٹ ورک ٹریفک کی نگرانی کر سکتی ہے۔ تفاصیل کیلئے تھپتھپائیں۔"</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"ایپس آپ کے مقام تک رسائی حاصل کر سکتی ہیں"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"‏Dpad بائیں کریں"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"‏Dpad دائیں کریں"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"‏Dpad سینٹر"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> کی کیپشن بار۔"</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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"اس کے کام کرنے کا طریقہ"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"زیر التواء..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"فنگر پرنٹ اَن لاک کو دوبارہ سیٹ اپ کریں"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"‫<xliff:g id="FINGERPRINT">%s</xliff:g> اچھی طرح کام نہیں کر رہا تھا اور حذف کر دیا گیا تھا"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"‫<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> اور <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> اچھی طرح کام نہیں کر رہے تھے اور انہیں حذف کر دیا گیا تھا"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> اچھی طرح کام نہیں کر رہا تھا اور حذف کر دیا گیا تھا۔ اپنے فون کو فنگر پرنٹ سے غیر مقفل کرنے کے لیے، اسے دوبارہ سیٹ اپ کریں۔"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> اور <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> اچھی طرح کام نہیں کر رہے تھے اور انہیں حذف کر دیا گیا تھا۔ اپنے فون کو اپنے فنگر پرنٹ سے غیر مقفل کرنے کے لیے انہیں دوبارہ سیٹ اپ کریں۔"</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"فیس اَن لاک کو دوبارہ سیٹ اپ کریں"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 0ef4ddc..517176b 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Bu qurilmada endi ishchi profilingiz mavjud emas"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Parol ko‘p marta xato kiritildi"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Administrator shaxsiy foydalanishga qoldirilgan qurilmani rad etdi"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Maxfiy makon olib tashlandi"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Tashkilotingiz mazkur boshqaruvdagi qurilmada maxfiy makon ochishni taqiqlagan."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Bu – boshqariladigan qurilma"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Tashkilotingiz bu qurilmani boshqaradi va tarmoq trafigini nazorat qilishi mumkin. Tafsilotlar uchun bosing."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Ilovalar joylashuv axborotidan foydalana oladi"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Ovozli yordam"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Qulflash"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Javob berish"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Yangi bildirishnoma"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Tashqi klaviatura"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Xavfsizlik"</string>
@@ -2195,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad – chapga"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad – oʻngga"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad – markazga"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g> taglavhalar 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>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Ishlash tartibi"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Kutilmoqda..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Barmoq izi bilan ochish funksiyasini qayta sozlang"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> yaxshi ishlamadi va oʻchirib tashlandi."</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> va <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> yaxshi ishlamadi va oʻchirib tashlandi."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> yaxshi ishlamadi va oʻchirib tashlandi. Telefonni barmoq izi bilan ochish uchun uni qayta sozlang."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> va <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> yaxshi ishlamadi va oʻchirib tashlandi. Telefonni barmoq izi bilan ochish uchun ularni qayta sozlang."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Yuz bilan ochishni qayta sozlash"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 4f2a9fd..e6db9dd 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Hồ sơ công việc của bạn không có sẵn trên thiết bị này nữa"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Quá nhiều lần nhập mật khẩu"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Quản trị viên đã từ bỏ quyền sở hữu thiết bị để cho phép dùng vào mục đích cá nhân"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Đã xoá không gian riêng tư"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Tổ chức của bạn không cho phép tạo không gian riêng tư trên thiết bị được quản lý này."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Thiết bị được quản lý"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Tổ chức của bạn sẽ quản lý thiết bị này và có thể theo dõi lưu lượng truy cập mạng. Nhấn để biết chi tiết."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Ứng dụng có thể truy cập vào thông tin vị trí của bạn"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Chuyển sang trái bằng bàn phím di chuyển"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Chuyển sang phải bằng bàn phím di chuyển"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Căn giữa bằng bàn phím di chuyển"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Thanh phụ đề của <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Cách hoạt động"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Đang chờ xử lý..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Thiết lập lại tính năng Mở khoá bằng vân tay"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"<xliff:g id="FINGERPRINT">%s</xliff:g> không dùng được và đã bị xoá"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> và <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> không dùng được và đã bị xoá"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"<xliff:g id="FINGERPRINT">%s</xliff:g> không dùng được và đã bị xoá. Hãy thiết lập lại để mở khoá điện thoại bằng vân tay."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> và <xliff:g id="FINGERPRINT_1">%2$s</xliff:g> không dùng được và đã bị xoá. Hãy thiết lập lại để mở khoá điện thoại bằng vân tay."</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Thiết lập lại tính năng Mở khoá bằng khuôn mặt"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 1024322..4b04940 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"您的工作资料已不在此设备上"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"密码尝试次数过多"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"管理员已将该设备开放给个人使用"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"私密空间已移除"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"贵组织不允许在此受管设备上使用私密空间。"</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"设备为受管理设备"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"贵单位会管理该设备,且可能会监控网络流量。点按即可了解详情。"</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"应用可以访问您的位置信息"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"语音助理"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"锁定"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"回复"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"新通知"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"实体键盘"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"安全性"</string>
@@ -2195,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"向左方向键"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"向右方向键"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"方向键中心"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"<xliff:g id="APP_NAME">%1$s</xliff:g>的标题栏。"</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>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"运作方式"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"待归档…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"重新设置指纹解锁功能"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"“<xliff:g id="FINGERPRINT">%s</xliff:g>”无法正常使用,已被删除"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"“<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>”和“<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>”无法正常使用,已被删除"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"“<xliff:g id="FINGERPRINT">%s</xliff:g>”无法正常使用,系统已将其删除。如要通过指纹解锁功能来解锁手机,请重新设置。"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"“<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>”和“<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>”无法正常使用,系统已将它们删除。如要通过指纹解锁功能来解锁手机,请重新设置。"</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"重新设置“人脸解锁”功能"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index baf9ef5..1aa9c72 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"你的工作設定檔無法再在此裝置上使用"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"密碼輸入錯誤的次數過多"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"管理員已開放裝置供個人使用"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"已移除私人空間"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"你的機構不允許在此受管理的裝置上建立私人空間。"</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"裝置已受管理"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"你的機構會管理此裝置,並可能會監控網絡流量。輕按即可瞭解詳情。"</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"應用程式可存取你的位置"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"十字鍵向左鍵"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"十字鍵向右鍵"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"十字鍵中心鍵"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」的說明列。"</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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"運作方式"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"待處理…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"重新設定「指紋解鎖」功能"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"由於「<xliff:g id="FINGERPRINT">%s</xliff:g>」無法正常運作,因此系統已將其刪除"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"由於「<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>」和「<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>」無法正常運作,因此系統已將其刪除。"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"由於「<xliff:g id="FINGERPRINT">%s</xliff:g>」無法正常運作,因此系統已將其刪除。請重新設定,才能使用指紋解鎖手機。"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"由於「<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>」和「<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>」無法正常運作,因此系統已將其刪除。請重新設定,才能使用指紋解鎖手機。"</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"重新設定「面孔解鎖」功能"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 9eeb6dc..1c83ee6 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"你的工作資料夾已不在這個裝置上"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"密碼輸入錯誤的次數過多"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"管理員將這部裝置開放給個人使用"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"私人空間已移除"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"貴機構不允許在這部受管理的裝置上建立私人空間。"</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"裝置受到管理"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"貴機構會管理這個裝置,且可能監控網路流量。輕觸即可瞭解詳情。"</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"應用程式可存取你的位置資訊"</string>
@@ -2194,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad 向左移"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Dpad 向右移"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Dpad 置中"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」的說明文字列。"</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>
@@ -2415,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"運作方式"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"待處理…"</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"重新設定指紋解鎖"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"「<xliff:g id="FINGERPRINT">%s</xliff:g>」無法正常運作,系統已將其刪除"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"「<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>」和「<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>」無法正常運作,系統已將其刪除"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"「<xliff:g id="FINGERPRINT">%s</xliff:g>」無法正常運作,因此系統已將其刪除。請重新設定,才能用指紋解鎖手機。"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"「<xliff:g id="FINGERPRINT_0">%1$s</xliff:g>」和「<xliff:g id="FINGERPRINT_1">%2$s</xliff:g>」無法正常運作,因此系統已將其刪除。請重新設定,才能用指紋解鎖手機。"</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"重新設定人臉解鎖"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 7749b1b..2375442 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -201,6 +201,8 @@
     <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"Iphrofayela yakho yomsebenzi ayisatholakali kule divayisi"</string>
     <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"Imizamo yamaphasiwedi eminingi kakhulu"</string>
     <string name="device_ownership_relinquished" msgid="4080886992183195724">"Umphathi udedela idivayisi ngokusetshenziswa komuntu siqu"</string>
+    <string name="private_space_deleted_by_admin" msgid="1484365588862066939">"Indawo engasese isusiwe"</string>
+    <string name="private_space_deleted_by_admin_details" msgid="7007781735201818689">"Inhlangano yakho ayivumeli izindawo zangasese kule divayisi ephethwe."</string>
     <string name="network_logging_notification_title" msgid="554983187553845004">"Idivayisi iphethwe"</string>
     <string name="network_logging_notification_text" msgid="1327373071132562512">"Inhlangano yakho iphethe le divayisi futhi kungenzeka ingaqaphi ithrafikhi yenethiwekhi. Thephela imininingwane."</string>
     <string name="location_changed_notification_title" msgid="3620158742816699316">"Izinhlelo zokusebenza zingakwazi ukufinyelela endaweni yakho"</string>
@@ -283,8 +285,7 @@
     <string name="global_action_voice_assist" msgid="6655788068555086695">"Isisekeli sezwi"</string>
     <string name="global_action_lockdown" msgid="2475471405907902963">"Khiya"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
-    <!-- no translation found for notification_compact_heads_up_reply (2425293958371284340) -->
-    <skip />
+    <string name="notification_compact_heads_up_reply" msgid="2425293958371284340">"Phendula"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"Isaziso esisha"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"Ikhibhodi ephathekayo"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"Ukuphepha"</string>
@@ -2195,7 +2196,6 @@
     <string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Ngakwesokunxele se-Dpad"</string>
     <string name="accessibility_system_action_dpad_right_label" msgid="9180196950365804081">"Ngakwesokudla se-Dpad"</string>
     <string name="accessibility_system_action_dpad_center_label" msgid="8149791419358224893">"Isikhungo se-Dpad"</string>
-    <string name="accessibility_freeform_caption" msgid="8377519323496290122">"Ibha yamazwibela we-<xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
     <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>
@@ -2416,10 +2416,8 @@
     <string name="satellite_notification_how_it_works" msgid="3132069321977520519">"Indlela esebenza ngayo"</string>
     <string name="unarchival_session_app_label" msgid="6811856981546348205">"Ilindile..."</string>
     <string name="fingerprint_dangling_notification_title" msgid="7362075195588639989">"Setha Ukuvula ngesigxivizo somunwe futhi"</string>
-    <!-- no translation found for fingerprint_dangling_notification_msg_1 (8517140433796229725) -->
-    <skip />
-    <!-- no translation found for fingerprint_dangling_notification_msg_2 (7578829498452127613) -->
-    <skip />
+    <string name="fingerprint_dangling_notification_msg_1" msgid="8517140433796229725">"I-<xliff:g id="FINGERPRINT">%s</xliff:g> ibingasebenzi kahle futhi isuliwe"</string>
+    <string name="fingerprint_dangling_notification_msg_2" msgid="7578829498452127613">"I-<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> kanye ne-<xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ibingasebenzi kahle futhi isuliwe"</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_1" msgid="2927018569542316055">"I-<xliff:g id="FINGERPRINT">%s</xliff:g> ibingasebenzi kahle futhi isuliwe. Phinde uyisethe ukuze uvule ifoni yakho ngesigxivizo somunwe."</string>
     <string name="fingerprint_dangling_notification_msg_all_deleted_2" msgid="6897989352716156176">"I-<xliff:g id="FINGERPRINT_0">%1$s</xliff:g> kanye ne-<xliff:g id="FINGERPRINT_1">%2$s</xliff:g> ibingasebenzi kahle futhi isuliwe. Phinde uyisethe ukuze uvule ifoni yakho ngesigxivizo somunwe wakho"</string>
     <string name="face_dangling_notification_title" msgid="947852541060975473">"Setha Ukuvula Ngobuso futhi"</string>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 97e753e..575573c 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -136,11 +136,6 @@
        <item>@color/search_url_text_material_light</item>
     </array>
 
-   <array name="preloaded_freeform_multi_window_drawables">
-      <item>@drawable/decor_maximize_button_dark</item>
-      <item>@drawable/decor_maximize_button_light</item>
-   </array>
-
     <!-- Used in LocalePicker -->
     <string-array translatable="false" name="special_locale_codes">
         <!-- http://b/17150708 - ensure that the list of languages says "Arabic"
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1112e65..0706b32 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4200,13 +4200,6 @@
          automatically try to pair with it when the device exits tablet mode. -->
     <string translatable="false" name="config_packagedKeyboardName"></string>
 
-    <!-- The device supports freeform window management. Windows have title bars and can be moved
-         and resized. If you set this to true, you also need to add
-         PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT feature to your device specification.
-         The duplication is necessary, because this information is used before the features are
-         available to the system.-->
-    <bool name="config_freeformWindowManagement">false</bool>
-
     <!-- If set, this will force all windows to draw the status bar background, including the apps
          that have not requested doing so (via the WindowManager.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS
          flag). -->
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 04f6f52..dcda5d8 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -403,4 +403,10 @@
     <integer name="config_wait_for_datagram_sending_response_for_last_message_timeout_millis">60000</integer>
     <java-symbol type="integer" name="config_wait_for_datagram_sending_response_for_last_message_timeout_millis" />
 
+    <!-- Boolean indicating whether Telephony should force PhoneGlobals creation
+         regardless of FEATURE_TELEPHONY presence.
+         -->
+    <bool name="config_force_phone_globals_creation">false</bool>
+    <java-symbol type="bool" name="config_force_phone_globals_creation" />
+
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a956a43..87141c7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5991,8 +5991,6 @@
     <string name="accessibility_system_action_dpad_right_label">Dpad Right</string>
     <!-- Label for Dpad center action [CHAR LIMIT=NONE] -->
     <string name="accessibility_system_action_dpad_center_label">Dpad Center</string>
-    <!-- Accessibility description of caption view -->
-    <string name="accessibility_freeform_caption">Caption bar of <xliff:g id="app_name">%1$s</xliff:g>.</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">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0d2fd1c..bb73934 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -402,7 +402,6 @@
   <java-symbol type="bool" name="config_supportMicNearUltrasound" />
   <java-symbol type="bool" name="config_supportSpeakerNearUltrasound" />
   <java-symbol type="bool" name="config_supportAudioSourceUnprocessed" />
-  <java-symbol type="bool" name="config_freeformWindowManagement" />
   <java-symbol type="bool" name="config_supportsBubble" />
   <java-symbol type="bool" name="config_supportsMultiWindow" />
   <java-symbol type="bool" name="config_supportsSplitScreenMultiWindow" />
@@ -1266,7 +1265,6 @@
   <java-symbol type="array" name="networkAttributes" />
   <java-symbol type="array" name="preloaded_color_state_lists" />
   <java-symbol type="array" name="preloaded_drawables" />
-  <java-symbol type="array" name="preloaded_freeform_multi_window_drawables" />
   <java-symbol type="array" name="sim_colors" />
   <java-symbol type="array" name="special_locale_codes" />
   <java-symbol type="array" name="special_locale_names" />
@@ -2470,16 +2468,6 @@
 
   <!-- From Phone -->
   <java-symbol type="bool" name="config_built_in_sip_phone" />
-  <java-symbol type="id" name="maximize_window" />
-  <java-symbol type="id" name="close_window" />
-  <java-symbol type="layout" name="decor_caption" />
-  <java-symbol type="drawable" name="decor_caption_title_focused" />
-  <java-symbol type="drawable" name="decor_close_button_dark" />
-  <java-symbol type="drawable" name="decor_close_button_light" />
-  <java-symbol type="drawable" name="decor_maximize_button_dark" />
-  <java-symbol type="drawable" name="decor_maximize_button_light" />
-  <java-symbol type="color" name="decor_button_dark_color" />
-  <java-symbol type="color" name="decor_button_light_color" />
   <java-symbol type="array" name="unloggable_phone_numbers" />
 
   <!-- From TelephonyProvider -->
@@ -4470,8 +4458,6 @@
   <java-symbol type="string" name="accessibility_system_action_dpad_right_label" />
   <java-symbol type="string" name="accessibility_system_action_dpad_center_label" />
 
-  <java-symbol type="string" name="accessibility_freeform_caption" />
-
   <!-- For Wide Color Gamut -->
   <java-symbol type="bool" name="config_enableWcgMode" />
 
diff --git a/core/tests/coretests/OWNERS b/core/tests/coretests/OWNERS
index b7e008b..b669e3b 100644
--- a/core/tests/coretests/OWNERS
+++ b/core/tests/coretests/OWNERS
@@ -3,3 +3,4 @@
 per-file BinderTest.java = file:platform/frameworks/native:/libs/binder/OWNERS
 per-file ParcelTest.java = file:platform/frameworks/native:/libs/binder/OWNERS
 per-file SurfaceControlRegistryTests.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file VintfObjectTest.java = file:platform/system/libvintf:/OWNERS
diff --git a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
index 89c2b3c..b972882 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityManagerTest.java
@@ -128,7 +128,8 @@
                 0x222222,                // colorBackground
                 0x333333,                // statusBarColor
                 0x444444,                // navigationBarColor
-                0,                       // statusBarAppearance
+                0x555555,                // systemBarsAppeareance
+                0x666666,                // topOpaqueSystemBarsAppeareance
                 true,                    // ensureStatusBarContrastWhenTransparent
                 true,                    // ensureNavigationBarContrastWhenTransparent
                 RESIZE_MODE_RESIZEABLE,  // resizeMode
@@ -153,7 +154,8 @@
                 0x222222,                  // colorBackground
                 0x333333,                  // statusBarColor
                 0x444444,                  // navigationBarColor
-                0,                         // statusBarAppearance
+                0x555555,                  // systemBarsAppeareance
+                0x666666,                  // topOpaqueSystemBarsAppeareance
                 false,                     // ensureStatusBarContrastWhenTransparent
                 false,                     // ensureNavigationBarContrastWhenTransparent
                 RESIZE_MODE_UNRESIZEABLE,  // resizeMode
@@ -169,7 +171,8 @@
                 0x2222222,               // colorBackground
                 0x3333332,               // statusBarColor
                 0x4444442,               // navigationBarColor
-                0,                       // statusBarAppearance
+                0x5555552,               // systemBarsAppeareance
+                0x6666662,               // topOpaqueSystemBarsAppeareance
                 true,                    // ensureStatusBarContrastWhenTransparent
                 true,                    // ensureNavigationBarContrastWhenTransparent
                 RESIZE_MODE_RESIZEABLE,  // resizeMode
@@ -200,7 +203,8 @@
                 0x222222,                  // colorBackground
                 0x333333,                  // statusBarColor
                 0x444444,                  // navigationBarColor
-                0,                         // statusBarAppearance
+                0x555555,                  // systemBarsAppeareance
+                0x666666,                  // topOpaqueSystemBarsAppeareance
                 false,                     // ensureStatusBarContrastWhenTransparent
                 false,                     // ensureNavigationBarContrastWhenTransparent
                 RESIZE_MODE_UNRESIZEABLE,  // resizeMode
@@ -223,7 +227,8 @@
                 0x222222,                  // colorBackground
                 0x333333,                  // statusBarColor
                 0x444444,                  // navigationBarColor
-                0,                         // statusBarAppearance
+                0x555555,                  // systemBarsAppeareance
+                0x666666,                  // topOpaqueSystemBarsAppeareance
                 false,                     // ensureStatusBarContrastWhenTransparent
                 false,                     // ensureNavigationBarContrastWhenTransparent
                 RESIZE_MODE_UNRESIZEABLE,  // resizeMode
@@ -256,6 +261,8 @@
             assertEquals(td1.getStatusBarColor(), td2.getStatusBarColor());
             assertEquals(td1.getNavigationBarColor(), td2.getNavigationBarColor());
             assertEquals(td1.getSystemBarsAppearance(), td2.getSystemBarsAppearance());
+            assertEquals(td1.getTopOpaqueSystemBarsAppearance(),
+                    td2.getTopOpaqueSystemBarsAppearance());
             assertEquals(td1.getResizeMode(), td2.getResizeMode());
             assertEquals(td1.getMinWidth(), td2.getMinWidth());
             assertEquals(td1.getMinHeight(), td2.getMinHeight());
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
index 3735274..c7060ad 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionItemTest.java
@@ -23,6 +23,8 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
 import android.app.Activity;
@@ -39,7 +41,6 @@
 import android.view.InsetsState;
 import android.window.ActivityWindowInfo;
 import android.window.ClientWindowFrames;
-import android.window.WindowContext;
 import android.window.WindowContextInfo;
 
 import androidx.test.filters.SmallTest;
@@ -73,8 +74,6 @@
     @Mock
     private IBinder mWindowClientToken;
     @Mock
-    private WindowContext mWindowContext;
-    @Mock
     private IWindow mWindow;
 
     // Can't mock final class.
@@ -176,4 +175,17 @@
 
         verify(mWindow).insetsControlChanged(mInsetsState, mActiveControls);
     }
+
+    @Test
+    public void testWindowStateInsetsControlChangeItem_executeError() throws RemoteException {
+        doThrow(new RemoteException()).when(mWindow).insetsControlChanged(any(), any());
+
+        mActiveControls = spy(mActiveControls);
+        final WindowStateInsetsControlChangeItem item = WindowStateInsetsControlChangeItem.obtain(
+                mWindow, mInsetsState, mActiveControls);
+        item.mActiveControls = mActiveControls;
+        item.execute(mHandler, mPendingActions);
+
+        verify(mActiveControls).release();
+    }
 }
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
index 548b8ec..8071d3d 100644
--- a/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteRawStatementTest.java
@@ -19,6 +19,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.junit.Assert.fail;
 
@@ -507,6 +508,12 @@
                 s.bindInt(1, 3);
                 s.step();
                 s.reset();
+                // Bind a zero-length blob
+                s.clearBindings();
+                s.bindInt(1, 4);
+                s.bindBlob(2, new byte[0]);
+                s.step();
+                s.reset();
             }
             mDatabase.setTransactionSuccessful();
         } finally {
@@ -545,6 +552,17 @@
                 for (int i = 0; i < c.length; i++) c[i] = 0;
                 s.bindInt(1, 3);
                 assertTrue(s.step());
+                assertNull(s.getColumnBlob(0));
+                assertEquals(0, s.readColumnBlob(0, c, 0, c.length, 0));
+                for (int i = 0; i < c.length; i++) assertEquals(0, c[i]);
+                s.reset();
+
+                // Fetch the zero-length blob
+                s.bindInt(1, 4);
+                assertTrue(s.step());
+                byte[] r = s.getColumnBlob(0);
+                assertNotNull(r);
+                assertEquals(0, r.length);
                 assertEquals(0, s.readColumnBlob(0, c, 0, c.length, 0));
                 for (int i = 0; i < c.length; i++) assertEquals(0, c[i]);
                 s.reset();
@@ -572,6 +590,83 @@
     }
 
     @Test
+    public void testText() {
+        mDatabase.beginTransaction();
+        try {
+            final String query = "CREATE TABLE t1 (i int, b text)";
+            try (SQLiteRawStatement s = mDatabase.createRawStatement(query)) {
+                assertFalse(s.step());
+            }
+            mDatabase.setTransactionSuccessful();
+        } finally {
+            mDatabase.endTransaction();
+        }
+
+        // Insert data into the table.
+        mDatabase.beginTransaction();
+        try {
+            final String query = "INSERT INTO t1 (i, b) VALUES (?1, ?2)";
+            try (SQLiteRawStatement s = mDatabase.createRawStatement(query)) {
+                // Bind a string
+                s.bindInt(1, 1);
+                s.bindText(2, "text");
+                s.step();
+                s.reset();
+                s.clearBindings();
+
+                // Bind a zero-length string
+                s.bindInt(1, 2);
+                s.bindText(2, "");
+                s.step();
+                s.reset();
+                s.clearBindings();
+
+                // Bind a null string
+                s.clearBindings();
+                s.bindInt(1, 3);
+                s.step();
+                s.reset();
+                s.clearBindings();
+            }
+            mDatabase.setTransactionSuccessful();
+        } finally {
+            mDatabase.endTransaction();
+        }
+
+        // Read back data and verify it against the reference copy.
+        mDatabase.beginTransactionReadOnly();
+        try {
+            final String query = "SELECT (b) FROM t1 WHERE i = ?1";
+            try (SQLiteRawStatement s = mDatabase.createRawStatement(query)) {
+                // Fetch the entire reference array.
+                s.bindInt(1, 1);
+                assertTrue(s.step());
+                assertEquals(SQLiteRawStatement.SQLITE_DATA_TYPE_TEXT, s.getColumnType(0));
+
+                String a = s.getColumnText(0);
+                assertNotNull(a);
+                assertEquals(a, "text");
+                s.reset();
+
+                s.bindInt(1, 2);
+                assertTrue(s.step());
+                String b = s.getColumnText(0);
+                assertNotNull(b);
+                assertEquals(b, "");
+                s.reset();
+
+                s.bindInt(1, 3);
+                assertTrue(s.step());
+                String c = s.getColumnText(0);
+                assertNull(c);
+                s.reset();
+            }
+        } finally {
+            mDatabase.endTransaction();
+        }
+    }
+
+    @Test
     public void testParameterMetadata() {
         createComplexDatabase();
 
diff --git a/core/tests/coretests/src/android/text/LayoutTest.java b/core/tests/coretests/src/android/text/LayoutTest.java
index 1c12362..98f8b7f 100644
--- a/core/tests/coretests/src/android/text/LayoutTest.java
+++ b/core/tests/coretests/src/android/text/LayoutTest.java
@@ -39,6 +39,7 @@
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.text.Layout.Alignment;
+import android.text.style.ForegroundColorSpan;
 import android.text.style.StrikethroughSpan;
 
 import androidx.test.filters.SmallTest;
@@ -933,6 +934,83 @@
         expect.that(numBackgroundsFound).isEqualTo(backgroundRectsDrawn);
     }
 
+    @Test
+    @RequiresFlagsEnabled(FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+    public void highContrastTextEnabled_testDrawMulticolorText_drawsBlackAndWhiteBackgrounds() {
+        /*
+        Here's what the final render should look like:
+
+       Text  |   Background
+     ========================
+        al   |    BW
+        w    |    WW
+        ei   |    WW
+        \t;  |    WW
+        s    |    BB
+        df   |    BB
+        s    |    BB
+        df   |    BB
+        @    |    BB
+      ------------------------
+         */
+
+        mTextPaint.setColor(Color.WHITE);
+
+        mSpannedText.setSpan(
+                // Can't use DKGREY because it is right on the cusp of clamping white
+                new ForegroundColorSpan(0xFF332211),
+                /* start= */ 1,
+                /* end= */ 6,
+                Spanned.SPAN_INCLUSIVE_EXCLUSIVE
+        );
+        mSpannedText.setSpan(
+                new ForegroundColorSpan(Color.LTGRAY),
+                /* start= */ 8,
+                /* end= */ 11,
+                Spanned.SPAN_INCLUSIVE_EXCLUSIVE
+        );
+        Layout layout = new StaticLayout(mSpannedText, mTextPaint, mWidth,
+                mAlign, mSpacingMult, mSpacingAdd, /* includePad= */ false);
+
+        final int width = 256;
+        final int height = 256;
+        MockCanvas c = new MockCanvas(width, height);
+        c.setHighContrastTextEnabled(true);
+        layout.draw(
+                c,
+                /* highlightPaths= */ null,
+                /* highlightPaints= */ null,
+                /* selectionPath= */ null,
+                /* selectionPaint= */ null,
+                /* cursorOffsetVertical= */ 0
+        );
+        List<MockCanvas.DrawCommand> drawCommands = c.getDrawCommands();
+        var highlightsDrawn = 0;
+        var numColorChangesWithinOneLine = 1;
+        var textsDrawn = STATIC_LINE_COUNT + numColorChangesWithinOneLine;
+        var backgroundRectsDrawn = STATIC_LINE_COUNT + numColorChangesWithinOneLine;
+        expect.withMessage("wrong number of drawCommands: " + drawCommands)
+                .that(drawCommands.size())
+                .isEqualTo(textsDrawn + backgroundRectsDrawn + highlightsDrawn);
+
+        var backgroundCommands = drawCommands.stream()
+                .filter(it -> it.rect != null)
+                .toList();
+
+        expect.that(backgroundCommands.get(0).paint.getColor()).isEqualTo(Color.BLACK);
+        expect.that(backgroundCommands.get(1).paint.getColor()).isEqualTo(Color.WHITE);
+        expect.that(backgroundCommands.get(2).paint.getColor()).isEqualTo(Color.WHITE);
+        expect.that(backgroundCommands.get(3).paint.getColor()).isEqualTo(Color.WHITE);
+        expect.that(backgroundCommands.get(4).paint.getColor()).isEqualTo(Color.WHITE);
+        expect.that(backgroundCommands.get(5).paint.getColor()).isEqualTo(Color.BLACK);
+        expect.that(backgroundCommands.get(6).paint.getColor()).isEqualTo(Color.BLACK);
+        expect.that(backgroundCommands.get(7).paint.getColor()).isEqualTo(Color.BLACK);
+        expect.that(backgroundCommands.get(8).paint.getColor()).isEqualTo(Color.BLACK);
+        expect.that(backgroundCommands.get(9).paint.getColor()).isEqualTo(Color.BLACK);
+
+        expect.that(backgroundCommands.size()).isEqualTo(backgroundRectsDrawn);
+    }
+
     private static final class MockCanvas extends Canvas {
 
         static class DrawCommand {
diff --git a/core/tests/coretests/src/android/text/SpanColorsTest.java b/core/tests/coretests/src/android/text/SpanColorsTest.java
new file mode 100644
index 0000000..3d8d8f9
--- /dev/null
+++ b/core/tests/coretests/src/android/text/SpanColorsTest.java
@@ -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 android.text;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.graphics.Color;
+import android.graphics.drawable.ShapeDrawable;
+import android.platform.test.annotations.Presubmit;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.ImageSpan;
+import android.text.style.UnderlineSpan;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class SpanColorsTest {
+    private final TextPaint mWorkPaint = new TextPaint();
+    private SpanColors mSpanColors;
+    private SpannableString mSpannedText;
+
+    @Before
+    public void setup() {
+        mSpanColors = new SpanColors();
+        mSpannedText = new SpannableString("Hello world! This is a test.");
+        mSpannedText.setSpan(new ForegroundColorSpan(Color.RED), 0, 4,
+                Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+        mSpannedText.setSpan(new ForegroundColorSpan(Color.GREEN), 6, 11,
+                Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+        mSpannedText.setSpan(new UnderlineSpan(), 5, 10, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+        mSpannedText.setSpan(new ImageSpan(new ShapeDrawable()), 1, 2,
+                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+        mSpannedText.setSpan(new ForegroundColorSpan(Color.BLUE), 12, 16,
+                Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+    }
+
+    @Test
+    public void testNoColorFound() {
+        mSpanColors.init(mWorkPaint, mSpannedText, 25, 30); // Beyond the spans
+        assertThat(mSpanColors.getColorAt(27)).isEqualTo(SpanColors.NO_COLOR_FOUND);
+    }
+
+    @Test
+    public void testSingleColorSpan() {
+        mSpanColors.init(mWorkPaint, mSpannedText, 1, 4);
+        assertThat(mSpanColors.getColorAt(3)).isEqualTo(Color.RED);
+    }
+
+    @Test
+    public void testMultipleColorSpans() {
+        mSpanColors.init(mWorkPaint, mSpannedText, 0, mSpannedText.length());
+        assertThat(mSpanColors.getColorAt(2)).isEqualTo(Color.RED);
+        assertThat(mSpanColors.getColorAt(5)).isEqualTo(SpanColors.NO_COLOR_FOUND);
+        assertThat(mSpanColors.getColorAt(8)).isEqualTo(Color.GREEN);
+        assertThat(mSpanColors.getColorAt(13)).isEqualTo(Color.BLUE);
+    }
+}
diff --git a/core/tests/coretests/src/android/util/BinaryXmlTest.java b/core/tests/coretests/src/android/util/BinaryXmlTest.java
index 025e831..da29828 100644
--- a/core/tests/coretests/src/android/util/BinaryXmlTest.java
+++ b/core/tests/coretests/src/android/util/BinaryXmlTest.java
@@ -24,6 +24,8 @@
 import static android.util.XmlTest.doVerifyWrite;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.fail;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
 import android.os.PersistableBundle;
@@ -41,12 +43,15 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
 
 @RunWith(AndroidJUnit4.class)
 public class BinaryXmlTest {
+    private static final int MAX_UNSIGNED_SHORT = 65_535;
+
     /**
      * Verify that we can write and read large numbers of interned
      * {@link String} values.
@@ -170,4 +175,49 @@
             }
         }
     }
+
+    @Test
+    public void testAttributeBytes_BinaryDataOverflow() throws Exception {
+        final TypedXmlSerializer out = Xml.newBinarySerializer();
+        final ByteArrayOutputStream os = new ByteArrayOutputStream();
+        out.setOutput(os, StandardCharsets.UTF_8.name());
+
+        final byte[] testBytes = new byte[MAX_UNSIGNED_SHORT + 1];
+        assertThrows(IOException.class,
+                () -> out.attributeBytesHex(/* namespace */ null, /* name */ "attributeBytesHex",
+                        testBytes));
+
+        assertThrows(IOException.class,
+                () -> out.attributeBytesBase64(/* namespace */ null, /* name */
+                        "attributeBytesBase64", testBytes));
+    }
+
+    @Test
+    public void testAttributeBytesHex_MaximumBinaryData() throws Exception {
+        final TypedXmlSerializer out = Xml.newBinarySerializer();
+        final ByteArrayOutputStream os = new ByteArrayOutputStream();
+        out.setOutput(os, StandardCharsets.UTF_8.name());
+
+        final byte[] testBytes = new byte[MAX_UNSIGNED_SHORT];
+        try {
+            out.attributeBytesHex(/* namespace */ null, /* name */ "attributeBytesHex", testBytes);
+        } catch (Exception e) {
+            fail("testAttributeBytesHex fails with exception: " + e.toString());
+        }
+    }
+
+    @Test
+    public void testAttributeBytesBase64_MaximumBinaryData() throws Exception {
+        final TypedXmlSerializer out = Xml.newBinarySerializer();
+        final ByteArrayOutputStream os = new ByteArrayOutputStream();
+        out.setOutput(os, StandardCharsets.UTF_8.name());
+
+        final byte[] testBytes = new byte[MAX_UNSIGNED_SHORT];
+        try {
+            out.attributeBytesBase64(/* namespace */ null, /* name */ "attributeBytesBase64",
+                    testBytes);
+        } catch (Exception e) {
+            fail("testAttributeBytesBase64 fails with exception: " + e.toString());
+        }
+    }
 }
diff --git a/core/tests/coretests/src/android/view/ContentRecordingSessionTest.java b/core/tests/coretests/src/android/view/ContentRecordingSessionTest.java
index 17980ac..f8800cb 100644
--- a/core/tests/coretests/src/android/view/ContentRecordingSessionTest.java
+++ b/core/tests/coretests/src/android/view/ContentRecordingSessionTest.java
@@ -18,6 +18,7 @@
 
 import static android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY;
 import static android.view.ContentRecordingSession.RECORD_CONTENT_TASK;
+import static android.view.ContentRecordingSession.TASK_ID_UNKNOWN;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 
@@ -45,6 +46,7 @@
 @Presubmit
 public class ContentRecordingSessionTest {
     private static final int DISPLAY_ID = 1;
+    private static final int TASK_ID = 123;
     private static final IBinder WINDOW_TOKEN = new Binder("DisplayContentWindowToken");
 
     @Test
@@ -65,6 +67,16 @@
         ContentRecordingSession session = ContentRecordingSession.createTaskSession(WINDOW_TOKEN);
         assertThat(session.getContentToRecord()).isEqualTo(RECORD_CONTENT_TASK);
         assertThat(session.getTokenToRecord()).isEqualTo(WINDOW_TOKEN);
+        assertThat(session.getTaskId()).isEqualTo(TASK_ID_UNKNOWN);
+    }
+
+    @Test
+    public void testSecondaryTaskConstructor() {
+        ContentRecordingSession session =
+                ContentRecordingSession.createTaskSession(WINDOW_TOKEN, TASK_ID);
+        assertThat(session.getContentToRecord()).isEqualTo(RECORD_CONTENT_TASK);
+        assertThat(session.getTokenToRecord()).isEqualTo(WINDOW_TOKEN);
+        assertThat(session.getTaskId()).isEqualTo(TASK_ID);
     }
 
     @Test
@@ -73,6 +85,7 @@
                 DEFAULT_DISPLAY);
         assertThat(session.getContentToRecord()).isEqualTo(RECORD_CONTENT_DISPLAY);
         assertThat(session.getTokenToRecord()).isNull();
+        assertThat(session.getTaskId()).isEqualTo(TASK_ID_UNKNOWN);
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index 0b1b40c..abe9c8e 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -41,11 +41,13 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.SystemClock;
+import android.platform.test.annotations.LargeTest;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.util.DisplayMetrics;
 import android.widget.FrameLayout;
+import android.widget.ProgressBar;
 
 import androidx.test.annotation.UiThreadTest;
 import androidx.test.filters.SmallTest;
@@ -162,7 +164,7 @@
         mActivityRule.runOnUiThread(() -> {
             mMovingView.setFrameContentVelocity(1f);
             mMovingView.invalidate();
-            runAfterDraw(() -> assertEquals(60f, mViewRoot.getLastPreferredFrameRate(), 0f));
+            runAfterDraw(() -> assertEquals(80f, mViewRoot.getLastPreferredFrameRate(), 0f));
         });
         waitForAfterDraw();
     }
@@ -188,7 +190,7 @@
             frameLayout.setFrameContentVelocity(1f);
             mMovingView.offsetTopAndBottom(100);
             frameLayout.invalidate();
-            runAfterDraw(() -> assertEquals(60f, mViewRoot.getLastPreferredFrameRate(), 0f));
+            runAfterDraw(() -> assertEquals(80f, mViewRoot.getLastPreferredFrameRate(), 0f));
         });
         waitForAfterDraw();
     }
@@ -433,7 +435,7 @@
             runAfterDraw(() -> {
                 assertEquals(FRAME_RATE_CATEGORY_LOW,
                         mViewRoot.getLastPreferredFrameRateCategory());
-                assertEquals(60f, mViewRoot.getLastPreferredFrameRate());
+                assertEquals(80f, mViewRoot.getLastPreferredFrameRate());
             });
         });
         waitForAfterDraw();
@@ -623,6 +625,162 @@
         assertEquals(FRAME_RATE_CATEGORY_HIGH_HINT, mViewRoot.getLastPreferredFrameRateCategory());
     }
 
+    @LargeTest
+    @Test
+    @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
+            FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY,
+            com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4
+    })
+    public void idleDetected() throws Throwable {
+        waitForFrameRateCategoryToSettle();
+        mActivityRule.runOnUiThread(() -> {
+            mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_HIGH);
+            mMovingView.setFrameContentVelocity(Float.MAX_VALUE);
+            mMovingView.invalidate();
+            runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_HIGH,
+                    mViewRoot.getLastPreferredFrameRateCategory()));
+        });
+        waitForAfterDraw();
+
+        // Wait for idle timeout
+        Thread.sleep(1000);
+        assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
+        assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
+                mViewRoot.getLastPreferredFrameRateCategory());
+    }
+
+    @LargeTest
+    @Test
+    @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
+            FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY,
+            com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4
+    })
+    public void vectorDrawableFrameRate() throws Throwable {
+        final ProgressBar[] progressBars = new ProgressBar[3];
+        final ViewGroup[] parents = new ViewGroup[1];
+        mActivityRule.runOnUiThread(() -> {
+            ViewGroup parent = (ViewGroup) mMovingView.getParent();
+            parents[0] = parent;
+            ProgressBar progressBar1 = new ProgressBar(mActivity);
+            parent.addView(progressBar1);
+            progressBar1.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW);
+            progressBar1.setIndeterminate(true);
+            progressBars[0] = progressBar1;
+
+            ProgressBar progressBar2 = new ProgressBar(mActivity);
+            parent.addView(progressBar2);
+            progressBar2.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NORMAL);
+            progressBar2.setIndeterminate(true);
+            progressBars[1] = progressBar2;
+
+            ProgressBar progressBar3 = new ProgressBar(mActivity);
+            parent.addView(progressBar3);
+            progressBar3.setRequestedFrameRate(45f);
+            progressBar3.setIndeterminate(true);
+            progressBars[2] = progressBar3;
+        });
+        waitForFrameRateCategoryToSettle();
+
+        // Wait for idle timeout
+        Thread.sleep(1000);
+        assertEquals(45f, mViewRoot.getLastPreferredFrameRate());
+        assertEquals(FRAME_RATE_CATEGORY_NORMAL, mViewRoot.getLastPreferredFrameRateCategory());
+
+        // Removing the vector drawable with NORMAL should drop the category to LOW
+        mActivityRule.runOnUiThread(() -> parents[0].removeView(progressBars[1]));
+        Thread.sleep(1000);
+        assertEquals(45f, mViewRoot.getLastPreferredFrameRate());
+        assertEquals(FRAME_RATE_CATEGORY_LOW,
+                mViewRoot.getLastPreferredFrameRateCategory());
+        // Removing the one voting for frame rate should leave only the category
+        mActivityRule.runOnUiThread(() -> parents[0].removeView(progressBars[2]));
+        Thread.sleep(1000);
+        assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
+        assertEquals(FRAME_RATE_CATEGORY_LOW,
+                mViewRoot.getLastPreferredFrameRateCategory());
+        // Removing the last one should leave it with no preference
+        mActivityRule.runOnUiThread(() -> parents[0].removeView(progressBars[0]));
+        Thread.sleep(1000);
+        assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
+        assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
+                mViewRoot.getLastPreferredFrameRateCategory());
+    }
+
+    @LargeTest
+    @Test
+    @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
+            FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY,
+            com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4
+    })
+    public void renderNodeAnimatorFrameRateCanceled() throws Throwable {
+        mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+        waitForFrameRateCategoryToSettle();
+
+        RenderNodeAnimator[] renderNodeAnimator = new RenderNodeAnimator[1];
+        renderNodeAnimator[0] = new RenderNodeAnimator(RenderNodeAnimator.ALPHA, 0f);
+        renderNodeAnimator[0].setDuration(100000);
+
+        mActivityRule.runOnUiThread(() -> {
+            renderNodeAnimator[0].setTarget(mMovingView);
+            renderNodeAnimator[0].start();
+            mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW);
+            runAfterDraw(() -> {
+                assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
+                assertEquals(FRAME_RATE_CATEGORY_LOW,
+                        mViewRoot.getLastPreferredFrameRateCategory());
+            });
+        });
+        waitForAfterDraw();
+
+        mActivityRule.runOnUiThread(() -> {
+            renderNodeAnimator[0].cancel();
+        });
+
+        // Wait for idle timeout
+        Thread.sleep(1000);
+        assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
+        assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
+                mViewRoot.getLastPreferredFrameRateCategory());
+    }
+
+    @LargeTest
+    @Test
+    @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY,
+            FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY,
+            com.android.graphics.surfaceflinger.flags.Flags.FLAG_VRR_BUGFIX_24Q4
+    })
+    public void renderNodeAnimatorFrameRateRemoved() throws Throwable {
+        mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE);
+        waitForFrameRateCategoryToSettle();
+
+        RenderNodeAnimator[] renderNodeAnimator = new RenderNodeAnimator[1];
+        renderNodeAnimator[0] = new RenderNodeAnimator(RenderNodeAnimator.ALPHA, 0f);
+        renderNodeAnimator[0].setDuration(100000);
+
+        mActivityRule.runOnUiThread(() -> {
+            renderNodeAnimator[0].setTarget(mMovingView);
+            renderNodeAnimator[0].start();
+            mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW);
+            runAfterDraw(() -> {
+                assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
+                assertEquals(FRAME_RATE_CATEGORY_LOW,
+                        mViewRoot.getLastPreferredFrameRateCategory());
+            });
+        });
+        waitForAfterDraw();
+
+        mActivityRule.runOnUiThread(() -> {
+            ViewGroup parent = (ViewGroup) mMovingView.getParent();
+            assert parent != null;
+            parent.removeView(mMovingView);
+        });
+
+        Thread.sleep(1000);
+        assertEquals(0f, mViewRoot.getLastPreferredFrameRate());
+        assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE,
+                mViewRoot.getLastPreferredFrameRateCategory());
+    }
+
     private void runAfterDraw(@NonNull Runnable runnable) {
         Handler handler = new Handler(Looper.getMainLooper());
         mAfterDrawLatch = new CountDownLatch(1);
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index a7f8176..94e187a 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -1287,6 +1287,31 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY)
+    public void votePreferredFrameRate_velocityVotedAfterOnDraw() throws Throwable {
+        mView = new View(sContext);
+        double delta = 0.1;
+        float pixelsPerSecond = 1000_000;
+        float expectedFrameRate = 120;
+        attachViewToWindow(mView);
+        sInstrumentation.waitForIdleSync();
+        ViewRootImpl viewRoot = mView.getViewRootImpl();
+        waitForFrameRateCategoryToSettle(mView);
+
+        sInstrumentation.runOnMainSync(() -> {
+            mView.setFrameContentVelocity(pixelsPerSecond);
+            mView.invalidate();
+            assertEquals(0, viewRoot.getPreferredFrameRate(), delta);
+            assertEquals(0, viewRoot.getLastPreferredFrameRate(), delta);
+            runAfterDraw(() -> {
+                assertEquals(expectedFrameRate, viewRoot.getPreferredFrameRate(), delta);
+                assertEquals(expectedFrameRate, viewRoot.getLastPreferredFrameRate(), delta);
+            });
+        });
+        waitForAfterDraw();
+    }
+
+    @Test
     public void forceInvertOffDarkThemeOff_forceDarkModeDisabled() {
         mSetFlagsRule.enableFlags(FLAG_FORCE_INVERT_COLOR);
         ShellIdentityUtils.invokeWithShellPermissions(() -> {
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index b5c264c..5a6824b 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -146,6 +146,10 @@
 
     public void setMagnificationCallbackEnabled(int displayId, boolean enabled) {}
 
+    public boolean isMagnificationSystemUIConnected() {
+        return false;
+    }
+
     public boolean setSoftKeyboardShowMode(int showMode) {
         return false;
     }
diff --git a/core/tests/utiltests/src/com/android/internal/util/NewlineNormalizerTest.java b/core/tests/utiltests/src/com/android/internal/util/NewlineNormalizerTest.java
deleted file mode 100644
index bcdac61..0000000
--- a/core/tests/utiltests/src/com/android/internal/util/NewlineNormalizerTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.util;
-
-import static junit.framework.Assert.assertEquals;
-
-
-import android.platform.test.annotations.DisabledOnRavenwood;
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Test for {@link NewlineNormalizer}
- * @hide
- */
-@DisabledOnRavenwood(blockedBy = NewlineNormalizer.class)
-@RunWith(AndroidJUnit4.class)
-public class NewlineNormalizerTest {
-
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule();
-
-    @Test
-    public void testEmptyInput() {
-        assertEquals("", NewlineNormalizer.normalizeNewlines(""));
-    }
-
-    @Test
-    public void testSingleNewline() {
-        assertEquals("\n", NewlineNormalizer.normalizeNewlines("\n"));
-    }
-
-    @Test
-    public void testMultipleConsecutiveNewlines() {
-        assertEquals("\n", NewlineNormalizer.normalizeNewlines("\n\n\n\n\n"));
-    }
-
-    @Test
-    public void testNewlinesWithSpacesAndTabs() {
-        String input = "Line 1\n  \n \t \n\tLine 2";
-        // Adjusted expected output to include the tab character
-        String expected = "Line 1\n\tLine 2";
-        assertEquals(expected, NewlineNormalizer.normalizeNewlines(input));
-    }
-
-    @Test
-    public void testMixedNewlineCharacters() {
-        String input = "Line 1\r\nLine 2\u000BLine 3\fLine 4\u2028Line 5\u2029Line 6";
-        String expected = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6";
-        assertEquals(expected, NewlineNormalizer.normalizeNewlines(input));
-    }
-}
diff --git a/core/tests/utiltests/src/com/android/internal/util/NotificationBigTextNormalizerTest.java b/core/tests/utiltests/src/com/android/internal/util/NotificationBigTextNormalizerTest.java
new file mode 100644
index 0000000..1f2e24a
--- /dev/null
+++ b/core/tests/utiltests/src/com/android/internal/util/NotificationBigTextNormalizerTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import static junit.framework.Assert.assertEquals;
+
+
+import android.platform.test.annotations.DisabledOnRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test for {@link NotificationBigTextNormalizer}
+ * @hide
+ */
+@DisabledOnRavenwood(blockedBy = NotificationBigTextNormalizer.class)
+@RunWith(AndroidJUnit4.class)
+public class NotificationBigTextNormalizerTest {
+
+    @Rule
+    public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+
+    @Test
+    public void testEmptyInput() {
+        assertEquals("", NotificationBigTextNormalizer.normalizeBigText(""));
+    }
+
+    @Test
+    public void testSingleNewline() {
+        assertEquals("", NotificationBigTextNormalizer.normalizeBigText("\n"));
+    }
+
+    @Test
+    public void testMultipleConsecutiveNewlines() {
+        assertEquals("", NotificationBigTextNormalizer.normalizeBigText("\n\n\n\n\n"));
+    }
+
+    @Test
+    public void testNewlinesWithSpacesAndTabs() {
+        String input = "Line 1\n  \n \t \n\tLine 2";
+        // Adjusted expected output to include the tab character
+        String expected = "Line 1\nLine 2";
+        assertEquals(expected, NotificationBigTextNormalizer.normalizeBigText(input));
+    }
+
+    @Test
+    public void testMixedNewlineCharacters() {
+        String input = "Line 1\r\nLine 2\u000BLine 3\fLine 4\u2028Line 5\u2029Line 6";
+        String expected = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6";
+        assertEquals(expected, NotificationBigTextNormalizer.normalizeBigText(input));
+    }
+
+    @Test
+    public void testConsecutiveSpaces() {
+        // Only spaces
+        assertEquals("This is a test.", NotificationBigTextNormalizer.normalizeBigText("This"
+                + "              is   a                         test."));
+        // Zero width characters bw spaces.
+        assertEquals("This is a test.", NotificationBigTextNormalizer.normalizeBigText("This"
+                + "\u200B \u200B \u200B \u200B \u200B \u200B \u200B \u200Bis\uFEFF \uFEFF \uFEFF"
+                + " \uFEFFa \u034F \u034F \u034F \u034F \u034F \u034Ftest."));
+
+        // Invisible formatting characters bw spaces.
+        assertEquals("This is a test.", NotificationBigTextNormalizer.normalizeBigText("This"
+                + "\u2061 \u2061 \u2061 \u2061 \u2061 \u2061 \u2061 \u2061is\u206E \u206E \u206E"
+                + " \u206Ea \uFFFB \uFFFB \uFFFB \uFFFB \uFFFB \uFFFBtest."));
+        // Non breakable spaces
+        assertEquals("This is a test.", NotificationBigTextNormalizer.normalizeBigText("This"
+                + "\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0is\u2005 \u2005 \u2005"
+                + " \u2005a\u2005\u2005\u2005 \u2005\u2005\u2005test."));
+    }
+
+    @Test
+    public void testZeroWidthCharRemoval() {
+        // Test each character individually
+        char[] zeroWidthChars = { '\u200B', '\u200C', '\u200D', '\uFEFF', '\u034F' };
+
+        for (char c : zeroWidthChars) {
+            String input = "Test" + c + "string";
+            String expected = "Teststring";
+            assertEquals(expected, NotificationBigTextNormalizer.normalizeBigText(input));
+        }
+    }
+
+    @Test
+    public void testWhitespaceReplacement() {
+        assertEquals("This text has horizontal whitespace.",
+                NotificationBigTextNormalizer.normalizeBigText(
+                        "This\ttext\thas\thorizontal\twhitespace."));
+        assertEquals("This text has mixed whitespace.",
+                NotificationBigTextNormalizer.normalizeBigText(
+                        "This  text  has \u00A0 mixed\u2009whitespace."));
+        assertEquals("This text has leading and trailing whitespace.",
+                NotificationBigTextNormalizer.normalizeBigText(
+                        "\t This text has leading and trailing whitespace. \n"));
+    }
+
+    @Test
+    public void testInvisibleFormattingCharacterRemoval() {
+        // Test each character individually
+        char[] invisibleFormattingChars = {
+                '\u2060', '\u2061', '\u2062', '\u2063', '\u2064', '\u2065',
+                '\u206A', '\u206B', '\u206C', '\u206D', '\u206E', '\u206F',
+                '\uFFF9', '\uFFFA', '\uFFFB'
+        };
+
+        for (char c : invisibleFormattingChars) {
+            String input = "Test " + c + "string";
+            String expected = "Test string";
+            assertEquals(expected, NotificationBigTextNormalizer.normalizeBigText(input));
+        }
+    }
+    @Test
+    public void testNonBreakSpaceReplacement() {
+        // Test each character individually
+        char[] nonBreakSpaces = {
+                '\u00A0', '\u1680', '\u2000', '\u2001', '\u2002',
+                '\u2003', '\u2004', '\u2005', '\u2006', '\u2007',
+                '\u2008', '\u2009', '\u200A', '\u202F', '\u205F', '\u3000'
+        };
+
+        for (char c : nonBreakSpaces) {
+            String input = "Test" + c + "string";
+            String expected = "Test string";
+            assertEquals(expected, NotificationBigTextNormalizer.normalizeBigText(input));
+        }
+    }
+}
diff --git a/data/etc/core.protolog.pb b/data/etc/core.protolog.pb
index ddb706e..a105ba7 100644
--- a/data/etc/core.protolog.pb
+++ b/data/etc/core.protolog.pb
Binary files differ
diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml
index f9fb84d..7823277 100644
--- a/data/etc/preinstalled-packages-platform.xml
+++ b/data/etc/preinstalled-packages-platform.xml
@@ -134,19 +134,4 @@
     <install-in-user-type package="com.android.avatarpicker">
         <install-in user-type="FULL" />
     </install-in-user-type>
-
-    <!-- AiLabs Warp app pre-installed in hardware/google/pixel/common/pixel-common-device.mk -->
-    <install-in-user-type package="com.google.android.apps.warp">
-        <install-in user-type="FULL" />
-        <install-in user-type="PROFILE" />
-        <do-not-install-in user-type="android.os.usertype.profile.PRIVATE" />
-    </install-in-user-type>
-
-    <!-- Google Home app pre-installed on tangor devices in vendor/google/products/tangor_common.mk
-     -->
-    <install-in-user-type package="com.google.android.apps.chromecast.app">
-        <install-in user-type="FULL" />
-        <install-in user-type="PROFILE" />
-        <do-not-install-in user-type="android.os.usertype.profile.PRIVATE" />
-    </install-in-user-type>
 </config>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 80f143c..db68f95 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -2035,6 +2035,24 @@
       "group": "WM_DEBUG_WINDOW_TRANSITIONS",
       "at": "com\/android\/server\/wm\/PhysicalDisplaySwitchTransitionLauncher.java"
     },
+    "-1640401313436844534": {
+      "message": "Resetting frozen recents task list reason=app touch win=%s x=%d y=%d insetFrame=%s",
+      "level": "INFO",
+      "group": "WM_DEBUG_TASKS",
+      "at": "com\/android\/server\/wm\/RecentTasks.java"
+    },
+    "-8803811426486764449": {
+      "message": "Setting frozen recents task list",
+      "level": "INFO",
+      "group": "WM_DEBUG_TASKS",
+      "at": "com\/android\/server\/wm\/RecentTasks.java"
+    },
+    "4040735335719974079": {
+      "message": "Resetting frozen recents task list reason=timeout",
+      "level": "INFO",
+      "group": "WM_DEBUG_TASKS",
+      "at": "com\/android\/server\/wm\/RecentTasks.java"
+    },
     "3308140128142966415": {
       "message": "remove RecentTask %s when finishing user %d",
       "level": "INFO",
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 0650b78..211f74a 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -16,6 +16,7 @@
 
 package android.graphics;
 
+import android.animation.Animator;
 import android.annotation.BytesLong;
 import android.annotation.ColorInt;
 import android.annotation.FloatRange;
@@ -1639,7 +1640,7 @@
      */
     public interface AnimationHost {
         /** @hide */
-        void registerAnimatingRenderNode(RenderNode animator);
+        void registerAnimatingRenderNode(RenderNode renderNode, Animator animator);
 
         /** @hide */
         void registerVectorDrawableAnimator(NativeVectorDrawableAnimator animator);
@@ -1654,7 +1655,7 @@
             throw new IllegalStateException("Cannot start this animator on a detached view!");
         }
         nAddAnimator(mNativeRenderNode, animator.getNativeAnimator());
-        mAnimationHost.registerAnimatingRenderNode(this);
+        mAnimationHost.registerAnimatingRenderNode(this, animator);
     }
 
     /** @hide */
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 55f205b..d4bb461 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -1266,6 +1266,7 @@
         private final IntArray mPendingAnimationActions = new IntArray();
         private final AnimatedVectorDrawable mDrawable;
         private long mTotalDuration;
+        private AnimatorListener mThreadedRendererAnimatorListener;
 
         VectorDrawableAnimatorRT(AnimatedVectorDrawable drawable) {
             mDrawable = drawable;
@@ -1689,6 +1690,9 @@
             if (mListener != null) {
                 mListener.onAnimationStart(null);
             }
+            if (mThreadedRendererAnimatorListener != null) {
+                mThreadedRendererAnimatorListener.onAnimationStart(null);
+            }
         }
 
         // This should only be called after animator has been added to the RenderNode target.
@@ -1717,6 +1721,9 @@
             if (mListener != null) {
                 mListener.onAnimationStart(null);
             }
+            if (mThreadedRendererAnimatorListener != null) {
+                mThreadedRendererAnimatorListener.onAnimationStart(null);
+            }
         }
 
         @Override
@@ -1725,6 +1732,11 @@
         }
 
         @Override
+        public void setThreadedRendererAnimatorListener(AnimatorListener animatorListener) {
+            mThreadedRendererAnimatorListener = animatorListener;
+        }
+
+        @Override
         public boolean canReverse() {
             return mIsReversible;
         }
@@ -1788,6 +1800,9 @@
             if (mListener != null) {
                 mListener.onAnimationEnd(null);
             }
+            if (mThreadedRendererAnimatorListener != null) {
+                mThreadedRendererAnimatorListener.onAnimationEnd(null);
+            }
         }
 
         // onFinished: should be called from native
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index d359a90..3cff915 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -1149,6 +1149,8 @@
 
         /**
          * Sets the serial number used for the certificate of the generated key pair.
+         * To ensure compatibility with devices and certificate parsers, the value
+         * should be 20 bytes or shorter (see RFC 5280 section 4.1.2.2).
          *
          * <p>By default, the serial number is {@code 1}.
          */
diff --git a/ktfmt_includes.txt b/ktfmt_includes.txt
deleted file mode 100644
index 0ac6265..0000000
--- a/ktfmt_includes.txt
+++ /dev/null
@@ -1,740 +0,0 @@
-+services/permission
-+packages/SystemUI
--packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
--packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt
--packages/SystemUI/checks/src/com/android/internal/systemui/lint/BroadcastSentViaContextDetector.kt
--packages/SystemUI/checks/src/com/android/internal/systemui/lint/RegisterReceiverViaContextDetector.kt
--packages/SystemUI/checks/src/com/android/internal/systemui/lint/SoftwareBitmapDetector.kt
--packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
--packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/View.kt
--packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt
--packages/SystemUI/shared/src/com/android/systemui/flags/FlagListenable.kt
--packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
--packages/SystemUI/shared/src/com/android/systemui/flags/FlagSerializer.kt
--packages/SystemUI/shared/src/com/android/systemui/flags/FlagSettingsHelper.kt
--packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt
--packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionDarkness.kt
--packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonPositionCalculator.kt
--packages/SystemUI/shared/src/com/android/systemui/shared/system/UncaughtExceptionPreHandlerManager.kt
--packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.kt
--packages/SystemUI/shared/src/com/android/systemui/unfold/system/ActivityManagerActivityTypeProvider.kt
--packages/SystemUI/shared/src/com/android/systemui/unfold/system/DeviceStateManagerFoldProvider.kt
--packages/SystemUI/shared/src/com/android/systemui/unfold/system/SystemUnfoldSharedModule.kt
--packages/SystemUI/src-debug/com/android/systemui/flags/FlagsModule.kt
--packages/SystemUI/src-release/com/android/systemui/flags/FlagsModule.kt
--packages/SystemUI/src/com/android/keyguard/ActiveUnlockConfig.kt
--packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt
--packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
--packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt
--packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt
--packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherAnchor.kt
--packages/SystemUI/src/com/android/keyguard/clock/ClockPalette.kt
--packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
--packages/SystemUI/src/com/android/keyguard/mediator/ScreenOnCoordinator.kt
--packages/SystemUI/src/com/android/systemui/BootCompleteCache.kt
--packages/SystemUI/src/com/android/systemui/BootCompleteCacheImpl.kt
--packages/SystemUI/src/com/android/systemui/CameraAvailabilityListener.kt
--packages/SystemUI/src/com/android/systemui/ChooserSelector.kt
--packages/SystemUI/src/com/android/systemui/DarkReceiverImpl.kt
--packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
--packages/SystemUI/src/com/android/systemui/DualToneHandler.kt
--packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
--packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt
--packages/SystemUI/src/com/android/systemui/SystemUIAppComponentFactoryBase.kt
--packages/SystemUI/src/com/android/systemui/SystemUIInitializerFactory.kt
--packages/SystemUI/src/com/android/systemui/SystemUIInitializerImpl.kt
--packages/SystemUI/src/com/android/systemui/assist/AssistLogger.kt
--packages/SystemUI/src/com/android/systemui/assist/AssistantInvocationEvent.kt
--packages/SystemUI/src/com/android/systemui/assist/AssistantSessionEvent.kt
--packages/SystemUI/src/com/android/systemui/backup/BackupHelper.kt
--packages/SystemUI/src/com/android/systemui/biometrics/AlternateUdfpsTouchProvider.kt
--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceIconController.kt
--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.kt
--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceIconController.kt
--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt
--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt
--packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt
--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt
--packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt
--packages/SystemUI/src/com/android/systemui/biometrics/DwellRippleShader.kt
--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt
--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt
--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.kt
--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.kt
--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt
--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt
--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHapticsSimulator.kt
--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt
--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
--packages/SystemUI/src/com/android/systemui/biometrics/Utils.kt
--packages/SystemUI/src/com/android/systemui/biometrics/dagger/BiometricsModule.kt
--packages/SystemUI/src/com/android/systemui/broadcast/ActionReceiver.kt
--packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
--packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcherStartable.kt
--packages/SystemUI/src/com/android/systemui/broadcast/BroadcastSender.kt
--packages/SystemUI/src/com/android/systemui/broadcast/PendingRemovalStore.kt
--packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt
--packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt
--packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt
--packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt
--packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt
--packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLogger.kt
--packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLoggerImpl.kt
--packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt
--packages/SystemUI/src/com/android/systemui/controls/CustomIconCache.kt
--packages/SystemUI/src/com/android/systemui/controls/TooltipManager.kt
--packages/SystemUI/src/com/android/systemui/controls/controller/ControlInfo.kt
--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapper.kt
--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfiguration.kt
--packages/SystemUI/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImpl.kt
--packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt
--packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt
--packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsComponent.kt
--packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsFeatureEnabled.kt
--packages/SystemUI/src/com/android/systemui/controls/dagger/ControlsModule.kt
--packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
--packages/SystemUI/src/com/android/systemui/controls/management/AppAdapter.kt
--packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt
--packages/SystemUI/src/com/android/systemui/controls/management/ControlsAnimations.kt
--packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
--packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
--packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingController.kt
--packages/SystemUI/src/com/android/systemui/controls/management/ControlsListingControllerImpl.kt
--packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
--packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
--packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt
--packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestReceiver.kt
--packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt
--packages/SystemUI/src/com/android/systemui/controls/management/ManagementPageIndicator.kt
--packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt
--packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt
--packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt
--packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt
--packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
--packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
--packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
--packages/SystemUI/src/com/android/systemui/controls/ui/ControlWithState.kt
--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
--packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
--packages/SystemUI/src/com/android/systemui/controls/ui/CornerDrawable.kt
--packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
--packages/SystemUI/src/com/android/systemui/controls/ui/RenderInfo.kt
--packages/SystemUI/src/com/android/systemui/controls/ui/StatusBehavior.kt
--packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
--packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt
--packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
--packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
--packages/SystemUI/src/com/android/systemui/controls/ui/TouchBehavior.kt
--packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
--packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderFactory.kt
--packages/SystemUI/src/com/android/systemui/decor/CutoutDecorProviderImpl.kt
--packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt
--packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt
--packages/SystemUI/src/com/android/systemui/decor/FaceScanningProviderFactory.kt
--packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt
--packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt
--packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt
--packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt
--packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt
--packages/SystemUI/src/com/android/systemui/demomode/DemoModeAvailabilityTracker.kt
--packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
--packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt
--packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt
--packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt
--packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
--packages/SystemUI/src/com/android/systemui/dump/DumpsysTableLogger.kt
--packages/SystemUI/src/com/android/systemui/dump/LogBufferEulogizer.kt
--packages/SystemUI/src/com/android/systemui/dump/LogBufferFreezer.kt
--packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt
--packages/SystemUI/src/com/android/systemui/flags/Flags.kt
--packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt
--packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt
--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
--packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt
--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt
--packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartable.kt
--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
--packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt
--packages/SystemUI/src/com/android/systemui/media/MediaProjectionCaptureTarget.kt
--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogFactory.kt
--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt
--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt
--packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionCli.kt
--packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManager.kt
--packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerFactory.kt
--packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitLogger.kt
--packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesLogger.kt
--packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesManager.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttFlags.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLogger.kt
--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorResultHandler.kt
--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt
--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTasksAdapter.kt
--packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolver.kt
--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
--packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt
--packages/SystemUI/src/com/android/systemui/power/BatteryStateSnapshot.kt
--packages/SystemUI/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitor.kt
--packages/SystemUI/src/com/android/systemui/privacy/MediaProjectionPrivacyItemMonitor.kt
--packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt
--packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt
--packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipEvent.kt
--packages/SystemUI/src/com/android/systemui/privacy/PrivacyConfig.kt
--packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
--packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt
--packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogEvent.kt
--packages/SystemUI/src/com/android/systemui/privacy/PrivacyItem.kt
--packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt
--packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
--packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
--packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
--packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt
--packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt
--packages/SystemUI/src/com/android/systemui/qs/QSExpansionPathInterpolator.kt
--packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt
--packages/SystemUI/src/com/android/systemui/qs/QSSquishinessController.kt
--packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt
--packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt
--packages/SystemUI/src/com/android/systemui/qs/VisibilityChangedDispatcher.kt
--packages/SystemUI/src/com/android/systemui/qs/carrier/CellSignalState.kt
--packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileView.kt
--packages/SystemUI/src/com/android/systemui/qs/external/CustomTileStatePersister.kt
--packages/SystemUI/src/com/android/systemui/qs/external/QSExternalModule.kt
--packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
--packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialogEventLogger.kt
--packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt
--packages/SystemUI/src/com/android/systemui/qs/tileimpl/HeightOverrideable.kt
--packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt
--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
--packages/SystemUI/src/com/android/systemui/qs/tiles/AlarmTile.kt
--packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
--packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt
--packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
--packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt
--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicy.kt
--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicyImpl.kt
--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotProxyService.kt
--packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseDialog.kt
--packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
--packages/SystemUI/src/com/android/systemui/settings/UserContentResolverProvider.kt
--packages/SystemUI/src/com/android/systemui/settings/UserContextProvider.kt
--packages/SystemUI/src/com/android/systemui/settings/UserFileManager.kt
--packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
--packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessMirrorHandler.kt
--packages/SystemUI/src/com/android/systemui/settings/brightness/MirroredBrightnessController.kt
--packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManager.kt
--packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt
--packages/SystemUI/src/com/android/systemui/shade/NPVCDownEventState.kt
--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt
--packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
--packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
--packages/SystemUI/src/com/android/systemui/shade/ShadeHeightLogger.kt
--packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
--packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt
--packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt
--packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt
--packages/SystemUI/src/com/android/systemui/smartspace/SmartspacePrecondition.kt
--packages/SystemUI/src/com/android/systemui/smartspace/SmartspaceTargetFilter.kt
--packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt
--packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceViewComponent.kt
--packages/SystemUI/src/com/android/systemui/smartspace/filters/LockscreenAndDreamTargetFilter.kt
--packages/SystemUI/src/com/android/systemui/smartspace/preconditions/LockscreenPrecondition.kt
--packages/SystemUI/src/com/android/systemui/statusbar/AbstractLockscreenShadeTransitionController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt
--packages/SystemUI/src/com/android/systemui/statusbar/LockScreenShadeOverScroller.kt
--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeScrimTransitionController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/MediaArtworkProcessor.kt
--packages/SystemUI/src/com/android/systemui/statusbar/NotificationClickNotifier.kt
--packages/SystemUI/src/com/android/systemui/statusbar/NotificationInteractionTracker.kt
--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
--packages/SystemUI/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScroller.kt
--packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt
--packages/SystemUI/src/com/android/systemui/statusbar/commandline/CommandRegistry.kt
--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/AccessPointController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ConnectivityState.kt
--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalControllerFactory.kt
--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileState.kt
--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileStatusTrackerFactory.kt
--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt
--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiState.kt
--packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiStatusTrackerFactory.kt
--packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
--packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartCentralSurfacesModule.kt
--packages/SystemUI/src/com/android/systemui/statusbar/disableflags/DisableFlagsLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt
--packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt
--packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt
--packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
--packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
--packages/SystemUI/src/com/android/systemui/statusbar/gesture/GesturePointerEventDetector.kt
--packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt
--packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt
--packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/gesture/TapGestureDetector.kt
--packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/FeedbackIcon.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/ViewGroupFadeHelper.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListAttachState.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImpl.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipeline.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographer.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/PipelineDumper.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/SuppressedAttachState.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolver.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DebugModeCoordinator.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinator.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinator.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinator.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinator.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinator.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinator.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinator.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/dagger/CoordinatorsModule.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManager.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/BindEventManagerImpl.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifInflater.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustment.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifStabilityManager.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTracker.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifEvent.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtender.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/DebugModeFilterProvider.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/NotificationVisibilityProviderImpl.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionHeaderVisibilityProvider.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/provider/SectionStyleProvider.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilder.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGroupController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewListener.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifGutsViewManager.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifRowController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifShadeEventSource.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifStackController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewBarn.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewRenderer.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotificationVisibilityProvider.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderExtensions.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationSectionHeadersModule.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalker.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/NotificationPersonExtractor.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ChannelEditorListView.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/RemoteInputViewModule.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationCallTemplateViewWrapper.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationConversationTemplateViewWrapper.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationPriorityBucket.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt
--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaViewController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculator.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProvider.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHideIconsForBouncerManager.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLocationPublisher.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarIconBlocklist.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarSystemEventAnimator.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometer.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallFlags.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt
--packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt
--packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt
--packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
--packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
--packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplyState.kt
--packages/SystemUI/src/com/android/systemui/statusbar/policy/InflatedSmartReplyViewHolder.kt
--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisabler.kt
--packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputViewController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
--packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateView.kt
--packages/SystemUI/src/com/android/systemui/statusbar/policy/VariableDateViewController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/policy/WalletController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/policy/WalletControllerImpl.kt
--packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/RemoteInput.kt
--packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/SmartRepliesInflationModule.kt
--packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowModule.kt
--packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt
--packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
--packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt
--packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarRootView.kt
--packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt
--packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt
--packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
--packages/SystemUI/src/com/android/systemui/unfold/FoldStateLogger.kt
--packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt
--packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
--packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt
--packages/SystemUI/src/com/android/systemui/user/UserSwitcherPopupMenu.kt
--packages/SystemUI/src/com/android/systemui/user/UserSwitcherRootView.kt
--packages/SystemUI/src/com/android/systemui/util/AsyncActivityLauncher.kt
--packages/SystemUI/src/com/android/systemui/util/ColorUtil.kt
--packages/SystemUI/src/com/android/systemui/util/ConvenienceExtensions.kt
--packages/SystemUI/src/com/android/systemui/util/DelayableMarqueeTextView.kt
--packages/SystemUI/src/com/android/systemui/util/DumpUtils.kt
--packages/SystemUI/src/com/android/systemui/util/InitializationChecker.kt
--packages/SystemUI/src/com/android/systemui/util/LargeScreenUtils.kt
--packages/SystemUI/src/com/android/systemui/util/ListenerSet.kt
--packages/SystemUI/src/com/android/systemui/util/NeverExactlyLinearLayout.kt
--packages/SystemUI/src/com/android/systemui/util/NoRemeasureMotionLayout.kt
--packages/SystemUI/src/com/android/systemui/util/PluralMessageFormater.kt
--packages/SystemUI/src/com/android/systemui/util/RingerModeTracker.kt
--packages/SystemUI/src/com/android/systemui/util/RingerModeTrackerImpl.kt
--packages/SystemUI/src/com/android/systemui/util/RoundedCornerProgressDrawable.kt
--packages/SystemUI/src/com/android/systemui/util/SafeMarqueeTextView.kt
--packages/SystemUI/src/com/android/systemui/util/SparseArrayUtils.kt
--packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt
--packages/SystemUI/src/com/android/systemui/util/UserAwareController.kt
--packages/SystemUI/src/com/android/systemui/util/WallpaperController.kt
--packages/SystemUI/src/com/android/systemui/util/animation/AnimationUtil.kt
--packages/SystemUI/src/com/android/systemui/util/animation/MeasurementInput.kt
--packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt
--packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt
--packages/SystemUI/src/com/android/systemui/util/animation/UniqueObjectHostView.kt
--packages/SystemUI/src/com/android/systemui/util/concurrency/Execution.kt
--packages/SystemUI/src/com/android/systemui/util/concurrency/PendingTasksContainer.kt
--packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt
--packages/SystemUI/src/com/android/systemui/util/kotlin/CoroutinesModule.kt
--packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt
--packages/SystemUI/src/com/android/systemui/util/kotlin/IpcSerializer.kt
--packages/SystemUI/src/com/android/systemui/util/kotlin/nullability.kt
--packages/SystemUI/src/com/android/systemui/util/recycler/HorizontalSpacerItemDecoration.kt
--packages/SystemUI/src/com/android/systemui/util/view/ViewUtil.kt
--packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt
--packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialogReceiver.kt
--packages/SystemUI/src/com/android/systemui/volume/VolumePanelFactory.kt
--packages/SystemUI/tests/src/com/android/keyguard/ActiveUnlockConfigTest.kt
--packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt
--packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
--packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt
--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
--packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt
--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUserSwitcherAnchorTest.kt
--packages/SystemUI/tests/src/com/android/keyguard/clock/ClockPaletteTest.kt
--packages/SystemUI/tests/src/com/android/keyguard/clock/ViewPreviewerTest.kt
--packages/SystemUI/tests/src/com/android/keyguard/mediator/ScreenOnCoordinatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/BootCompleteCacheTest.kt
--packages/SystemUI/tests/src/com/android/systemui/ChooserSelectorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/DisplayCutoutBaseViewTest.kt
--packages/SystemUI/tests/src/com/android/systemui/InstanceIdSequenceFake.kt
--packages/SystemUI/tests/src/com/android/systemui/ScreenDecorHwcLayerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/animation/GhostedViewTransitionAnimatorControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/animation/ViewHierarchyAnimatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewTest.kt
--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceViewTest.kt
--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintViewTest.kt
--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
--packages/SystemUI/tests/src/com/android/systemui/broadcast/ActionReceiverTest.kt
--packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt
--packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastSenderTest.kt
--packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt
--packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt
--packages/SystemUI/tests/src/com/android/systemui/camera/CameraGestureHelperTest.kt
--packages/SystemUI/tests/src/com/android/systemui/camera/CameraIntentsTest.kt
--packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/controls/CustomIconCacheTest.kt
--packages/SystemUI/tests/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapperTest.kt
--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlActionCoordinatorImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt
--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
--packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
--packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt
--packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
--packages/SystemUI/tests/src/com/android/systemui/controls/management/AppAdapterTest.kt
--packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsListingControllerImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt
--packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt
--packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt
--packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt
--packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
--packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt
--packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt
--packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt
--packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt
--packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
--packages/SystemUI/tests/src/com/android/systemui/dump/DumpHandlerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/dump/DumpsysTableLoggerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferFreezerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt
--packages/SystemUI/tests/src/com/android/systemui/dump/LogEulogizerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugTest.kt
--packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt
--packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt
--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt
--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepositoryTest.kt
--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/lifecycle/InstantTaskExecutorRule.kt
--packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesManagerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLoggerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
--packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt
--packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyConfigFlagsTest.kt
--packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
--packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/QSContainerImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelSwitchToParentTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/SettingObserverTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/carrier/CellSignalStateTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogEventLoggerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/ResourceIconTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
--packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordDialogTest.kt
--packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
--packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt
--packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt
--packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/MediaArtworkProcessorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/VibratorHelperTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandRegistryTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableFlagsLoggerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableStateTrackerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManagerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifLiveDataStoreImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/TargetSdkResolverTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/GroupCountCoordinatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RemoteInputCoordinatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/RowAppearanceCoordinatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SensitiveContentCoordinatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/SmartspaceDedupingCoordinatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionInconsistencyTrackerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/notifcollection/SelfTrackingLifetimeExtenderTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/provider/VisualStabilityProviderTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ChannelEditorDialogControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/MediaContainerViewTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemBarAttributesListenerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ClockTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/FlashlightControllerImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
--packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt
--packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
--packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt
--packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
--packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt
--packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt
--packages/SystemUI/tests/src/com/android/systemui/user/UserCreatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/util/FakeSharedPreferencesTest.kt
--packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt
--packages/SystemUI/tests/src/com/android/systemui/util/ListenerSetTest.kt
--packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt
--packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt
--packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
--packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt
--packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt
--packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt
--packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt
--packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSharedPreferences.kt
--packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
--packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt
--packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt
--packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt
--packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt
--packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/SizeScreenStatusProvider.kt
--packages/SystemUI/unfold/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt
--packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldMain.kt
--packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt
--packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt
--packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt
--packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
index 29936cc..23dc96c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
@@ -200,6 +200,10 @@
             }
 
             // At this point, a divider is required.
+            final TaskFragmentContainer primaryContainer =
+                    topSplitContainer.getPrimaryContainer();
+            final TaskFragmentContainer secondaryContainer =
+                    topSplitContainer.getSecondaryContainer();
 
             // Create the decor surface if one is not available yet.
             final SurfaceControl decorSurface = parentInfo.getDecorSurface();
@@ -207,42 +211,43 @@
                 // Clean up when the decor surface is currently unavailable.
                 removeDivider();
                 // Request to create the decor surface
-                createOrMoveDecorSurfaceLocked(wct, topSplitContainer.getPrimaryContainer());
+                createOrMoveDecorSurfaceLocked(wct, primaryContainer);
                 return;
             }
 
             // Update the decor surface owner if needed.
             boolean isDraggableExpandType =
                     SplitAttributesHelper.isDraggableExpandType(splitAttributes);
-            final TaskFragmentContainer decorSurfaceOwnerContainer = isDraggableExpandType
-                    ? topSplitContainer.getSecondaryContainer()
-                    : topSplitContainer.getPrimaryContainer();
+            final TaskFragmentContainer decorSurfaceOwnerContainer =
+                    isDraggableExpandType ? secondaryContainer : primaryContainer;
 
             if (!Objects.equals(
                     mDecorSurfaceOwner, decorSurfaceOwnerContainer.getTaskFragmentToken())) {
                 createOrMoveDecorSurfaceLocked(wct, decorSurfaceOwnerContainer);
             }
-            final boolean isVerticalSplit = isVerticalSplit(topSplitContainer);
-            final boolean isReversedLayout = isReversedLayout(
-                    topSplitContainer.getCurrentSplitAttributes(),
-                    parentInfo.getConfiguration());
+
+            final Configuration parentConfiguration = parentInfo.getConfiguration();
+            final Rect taskBounds = parentConfiguration.windowConfiguration.getBounds();
+            final boolean isVerticalSplit = isVerticalSplit(splitAttributes);
+            final boolean isReversedLayout = isReversedLayout(splitAttributes, parentConfiguration);
+            final int dividerWidthPx = getDividerWidthPx(dividerAttributes);
 
             updateProperties(
                     new Properties(
-                            parentInfo.getConfiguration(),
+                            parentConfiguration,
                             dividerAttributes,
                             decorSurface,
                             getInitialDividerPosition(
-                                    topSplitContainer, isVerticalSplit, isReversedLayout),
+                                    primaryContainer, secondaryContainer, taskBounds,
+                                    dividerWidthPx, isDraggableExpandType, isVerticalSplit,
+                                    isReversedLayout),
                             isVerticalSplit,
                             isReversedLayout,
                             parentInfo.getDisplayId(),
                             isDraggableExpandType,
-                            getContainerBackgroundColor(topSplitContainer.getPrimaryContainer(),
-                                    DEFAULT_PRIMARY_VEIL_COLOR),
-                            getContainerBackgroundColor(topSplitContainer.getSecondaryContainer(),
-                                    DEFAULT_SECONDARY_VEIL_COLOR)
-                    ));
+                            primaryContainer,
+                            secondaryContainer)
+            );
         }
     }
 
@@ -338,32 +343,31 @@
 
     @VisibleForTesting
     static int getInitialDividerPosition(
-            @NonNull SplitContainer splitContainer,
+            @NonNull TaskFragmentContainer primaryContainer,
+            @NonNull TaskFragmentContainer secondaryContainer,
+            @NonNull Rect taskBounds,
+            int dividerWidthPx,
+            boolean isDraggableExpandType,
             boolean isVerticalSplit,
             boolean isReversedLayout) {
-        final Rect primaryBounds =
-                splitContainer.getPrimaryContainer().getLastRequestedBounds();
-        final Rect secondaryBounds =
-                splitContainer.getSecondaryContainer().getLastRequestedBounds();
-        final SplitAttributes splitAttributes = splitContainer.getCurrentSplitAttributes();
-
-        if (SplitAttributesHelper.isDraggableExpandType(splitAttributes)) {
-            // If the container is fully expanded by dragging the divider, we display the divider
-            // on the edge.
-            final int dividerWidth = getDividerWidthPx(splitAttributes.getDividerAttributes());
+        if (isDraggableExpandType) {
+            // If the secondary container is fully expanded by dragging the divider, we display the
+            // divider on the edge.
             final int fullyExpandedPosition = isVerticalSplit
-                    ? primaryBounds.right - dividerWidth
-                    : primaryBounds.bottom - dividerWidth;
+                    ? taskBounds.width() - dividerWidthPx
+                    : taskBounds.height() - dividerWidthPx;
             return isReversedLayout ? fullyExpandedPosition : 0;
         } else {
+            final Rect primaryBounds = primaryContainer.getLastRequestedBounds();
+            final Rect secondaryBounds = secondaryContainer.getLastRequestedBounds();
             return isVerticalSplit
                     ? Math.min(primaryBounds.right, secondaryBounds.right)
                     : Math.min(primaryBounds.bottom, secondaryBounds.bottom);
         }
     }
 
-    private static boolean isVerticalSplit(@NonNull SplitContainer splitContainer) {
-        final int layoutDirection = splitContainer.getCurrentSplitAttributes().getLayoutDirection();
+    private static boolean isVerticalSplit(@NonNull SplitAttributes splitAttributes) {
+        final int layoutDirection = splitAttributes.getLayoutDirection();
         switch (layoutDirection) {
             case SplitAttributes.LayoutDirection.LEFT_TO_RIGHT:
             case SplitAttributes.LayoutDirection.RIGHT_TO_LEFT:
@@ -510,11 +514,16 @@
             if (mProperties != null && mRenderer != null) {
                 final Rect taskBounds = mProperties.mConfiguration.windowConfiguration.getBounds();
                 mDividerPosition = calculateDividerPosition(
-                        event, taskBounds, mRenderer.mDividerWidthPx,
+                        event, taskBounds, mProperties.mDividerWidthPx,
                         mProperties.mDividerAttributes, mProperties.mIsVerticalSplit,
                         calculateMinPosition(), calculateMaxPosition());
                 mRenderer.setDividerPosition(mDividerPosition);
-                switch (event.getAction()) {
+
+                // Convert to use screen-based coordinates to prevent lost track of motion events
+                // while moving divider bar and calculating dragging velocity.
+                event.setLocation(event.getRawX(), event.getRawY());
+                final int action = event.getAction() & MotionEvent.ACTION_MASK;
+                switch (action) {
                     case MotionEvent.ACTION_DOWN:
                         onStartDragging(event);
                         break;
@@ -671,8 +680,8 @@
         final int minPosition = calculateMinPosition();
         final int maxPosition = calculateMaxPosition();
         final int fullyExpandedPosition = mProperties.mIsVerticalSplit
-                ? taskBounds.right - mRenderer.mDividerWidthPx
-                : taskBounds.bottom - mRenderer.mDividerWidthPx;
+                ? taskBounds.width() - mProperties.mDividerWidthPx
+                : taskBounds.height() - mProperties.mDividerWidthPx;
 
         if (isDraggingToFullscreenAllowed(mProperties.mDividerAttributes)) {
             final float displayDensity = getDisplayDensity();
@@ -713,9 +722,9 @@
             return snap(dividerPosition, possiblePositions);
         }
         if (velocity < 0) {
-            return 0;
+            return minPosition;
         } else {
-            return fullyExpandedPosition;
+            return maxPosition;
         }
     }
 
@@ -777,7 +786,7 @@
     private int calculateMinPosition() {
         return calculateMinPosition(
                 mProperties.mConfiguration.windowConfiguration.getBounds(),
-                mRenderer.mDividerWidthPx, mProperties.mDividerAttributes,
+                mProperties.mDividerWidthPx, mProperties.mDividerAttributes,
                 mProperties.mIsVerticalSplit, mProperties.mIsReversedLayout);
     }
 
@@ -785,7 +794,7 @@
     private int calculateMaxPosition() {
         return calculateMaxPosition(
                 mProperties.mConfiguration.windowConfiguration.getBounds(),
-                mRenderer.mDividerWidthPx, mProperties.mDividerAttributes,
+                mProperties.mDividerWidthPx, mProperties.mDividerAttributes,
                 mProperties.mIsVerticalSplit, mProperties.mIsReversedLayout);
     }
 
@@ -823,13 +832,12 @@
      * Returns the new split ratio of the {@link SplitContainer} based on the current divider
      * position.
      */
-    float calculateNewSplitRatio(@NonNull SplitContainer topSplitContainer) {
+    float calculateNewSplitRatio() {
         synchronized (mLock) {
             return calculateNewSplitRatio(
-                    topSplitContainer,
                     mDividerPosition,
                     mProperties.mConfiguration.windowConfiguration.getBounds(),
-                    mRenderer.mDividerWidthPx,
+                    mProperties.mDividerWidthPx,
                     mProperties.mIsVerticalSplit,
                     mProperties.mIsReversedLayout,
                     calculateMinPosition(),
@@ -841,21 +849,20 @@
     private static boolean isDraggingToFullscreenAllowed(
             @NonNull DividerAttributes dividerAttributes) {
         // TODO(b/293654166) Use DividerAttributes.isDraggingToFullscreenAllowed when extension is
-        // updated.
-        return true;
+        // updated to v7.
+        return false;
     }
 
     /**
      * Returns the new split ratio of the {@link SplitContainer} based on the current divider
      * position.
      *
-     * @param topSplitContainer the {@link SplitContainer} for which to compute the split ratio.
      * @param dividerPosition the divider position. See {@link #mDividerPosition}.
      * @param taskBounds the task bounds
      * @param dividerWidthPx the width of the divider in pixels.
      * @param isVerticalSplit if {@code true}, the split is a vertical split. If {@code false}, the
      *                        split is a horizontal split. See
-     *                        {@link #isVerticalSplit(SplitContainer)}.
+     *                        {@link #isVerticalSplit(SplitAttributes)}.
      * @param isReversedLayout if {@code true}, the split layout is reversed, i.e. right-to-left or
      *                         bottom-to-top. If {@code false}, the split is not reversed, i.e.
      *                         left-to-right or top-to-bottom. See
@@ -866,7 +873,6 @@
      */
     @VisibleForTesting
     static float calculateNewSplitRatio(
-            @NonNull SplitContainer topSplitContainer,
             int dividerPosition,
             @NonNull Rect taskBounds,
             int dividerWidthPx,
@@ -891,8 +897,6 @@
             dividerPosition = Math.clamp(dividerPosition, minPosition, maxPosition);
         }
 
-        final TaskFragmentContainer primaryContainer = topSplitContainer.getPrimaryContainer();
-        final Rect origPrimaryBounds = primaryContainer.getLastRequestedBounds();
         final int usableSize = isVerticalSplit
                 ? taskBounds.width() - dividerWidthPx
                 : taskBounds.height() - dividerWidthPx;
@@ -900,13 +904,13 @@
         final float newRatio;
         if (isVerticalSplit) {
             final int newPrimaryWidth = isReversedLayout
-                    ? (origPrimaryBounds.right - (dividerPosition + dividerWidthPx))
-                    : (dividerPosition - origPrimaryBounds.left);
+                    ? taskBounds.width() - (dividerPosition + dividerWidthPx)
+                    : dividerPosition;
             newRatio = 1.0f * newPrimaryWidth / usableSize;
         } else {
             final int newPrimaryHeight = isReversedLayout
-                    ? (origPrimaryBounds.bottom - (dividerPosition + dividerWidthPx))
-                    : (dividerPosition - origPrimaryBounds.top);
+                    ? taskBounds.height() - (dividerPosition + dividerWidthPx)
+                    : dividerPosition;
             newRatio = 1.0f * newPrimaryHeight / usableSize;
         }
         return newRatio;
@@ -959,8 +963,11 @@
         private final int mDisplayId;
         private final boolean mIsReversedLayout;
         private final boolean mIsDraggableExpandType;
-        private final Color mPrimaryVeilColor;
-        private final Color mSecondaryVeilColor;
+        @NonNull
+        private final TaskFragmentContainer mPrimaryContainer;
+        @NonNull
+        private final TaskFragmentContainer mSecondaryContainer;
+        private final int mDividerWidthPx;
 
         @VisibleForTesting
         Properties(
@@ -972,8 +979,8 @@
                 boolean isReversedLayout,
                 int displayId,
                 boolean isDraggableExpandType,
-                @NonNull Color primaryVeilColor,
-                @NonNull Color secondaryVeilColor) {
+                @NonNull TaskFragmentContainer primaryContainer,
+                @NonNull TaskFragmentContainer secondaryContainer) {
             mConfiguration = configuration;
             mDividerAttributes = dividerAttributes;
             mDecorSurface = decorSurface;
@@ -982,8 +989,9 @@
             mIsReversedLayout = isReversedLayout;
             mDisplayId = displayId;
             mIsDraggableExpandType = isDraggableExpandType;
-            mPrimaryVeilColor = primaryVeilColor;
-            mSecondaryVeilColor = secondaryVeilColor;
+            mPrimaryContainer = primaryContainer;
+            mSecondaryContainer = secondaryContainer;
+            mDividerWidthPx = getDividerWidthPx(dividerAttributes);
         }
 
         /**
@@ -1006,8 +1014,8 @@
                     && a.mDisplayId == b.mDisplayId
                     && a.mIsReversedLayout == b.mIsReversedLayout
                     && a.mIsDraggableExpandType == b.mIsDraggableExpandType
-                    && a.mPrimaryVeilColor.equals(b.mPrimaryVeilColor)
-                    && a.mSecondaryVeilColor.equals(b.mSecondaryVeilColor);
+                    && a.mPrimaryContainer == b.mPrimaryContainer
+                    && a.mSecondaryContainer == b.mSecondaryContainer;
         }
 
         private static boolean areSameSurfaces(
@@ -1050,7 +1058,6 @@
         private final View.OnTouchListener mListener;
         @NonNull
         private Properties mProperties;
-        private int mDividerWidthPx;
         private int mHandleWidthPx;
         @Nullable
         private SurfaceControl mPrimaryVeil;
@@ -1090,7 +1097,6 @@
         /** Updates the divider when initializing or when properties are changed */
         @VisibleForTesting
         void update() {
-            mDividerWidthPx = getDividerWidthPx(mProperties.mDividerAttributes);
             mDividerPosition = mProperties.mInitialDividerPosition;
             mWindowlessWindowManager.setConfiguration(mProperties.mConfiguration);
 
@@ -1156,15 +1162,17 @@
                 // When the divider drag handle width is larger than the divider width, the position
                 // of the divider surface is adjusted so that it is large enough to host both the
                 // divider line and the divider drag handle.
-                mDividerSurfaceWidthPx = Math.max(mDividerWidthPx, mHandleWidthPx);
+                mDividerSurfaceWidthPx = Math.max(mProperties.mDividerWidthPx, mHandleWidthPx);
+                dividerSurfacePosition = mProperties.mIsReversedLayout
+                        ? mDividerPosition
+                        : mDividerPosition + mProperties.mDividerWidthPx - mDividerSurfaceWidthPx;
                 dividerSurfacePosition =
-                        mProperties.mIsReversedLayout
-                                ? mDividerPosition
-                                : mDividerPosition + mDividerWidthPx - mDividerSurfaceWidthPx;
-                dividerSurfacePosition = Math.clamp(dividerSurfacePosition, 0,
-                        mProperties.mIsVerticalSplit ? taskBounds.width() : taskBounds.height());
+                        Math.clamp(dividerSurfacePosition, 0,
+                                mProperties.mIsVerticalSplit
+                                        ? taskBounds.width() - mDividerSurfaceWidthPx
+                                        : taskBounds.height() - mDividerSurfaceWidthPx);
             } else {
-                mDividerSurfaceWidthPx = mDividerWidthPx;
+                mDividerSurfaceWidthPx = mProperties.mDividerWidthPx;
                 dividerSurfacePosition = mDividerPosition;
             }
 
@@ -1177,16 +1185,9 @@
             }
 
             // Update divider line position in the surface
-            if (!mProperties.mIsReversedLayout) {
-                final int offset = mDividerPosition - dividerSurfacePosition;
-                mDividerLine.setX(mProperties.mIsVerticalSplit ? offset : 0);
-                mDividerLine.setY(mProperties.mIsVerticalSplit ? 0 : offset);
-            } else {
-                // For reversed layout, the divider line is always at the start of the divider
-                // surface.
-                mDividerLine.setX(0);
-                mDividerLine.setY(0);
-            }
+            final int offset = mDividerPosition - dividerSurfacePosition;
+            mDividerLine.setX(mProperties.mIsVerticalSplit ? offset : 0);
+            mDividerLine.setY(mProperties.mIsVerticalSplit ? 0 : offset);
 
             if (mIsDragging) {
                 updateVeils(t);
@@ -1236,8 +1237,10 @@
             final Rect taskBounds = mProperties.mConfiguration.windowConfiguration.getBounds();
             mDividerLine.setLayoutParams(
                     mProperties.mIsVerticalSplit
-                            ? new FrameLayout.LayoutParams(mDividerWidthPx, taskBounds.height())
-                            : new FrameLayout.LayoutParams(taskBounds.width(), mDividerWidthPx)
+                            ? new FrameLayout.LayoutParams(
+                                    mProperties.mDividerWidthPx, taskBounds.height())
+                            : new FrameLayout.LayoutParams(
+                                    taskBounds.width(), mProperties.mDividerWidthPx)
             );
             if (mProperties.mDividerAttributes.getDividerType()
                     == DividerAttributes.DIVIDER_TYPE_DRAGGABLE) {
@@ -1325,8 +1328,12 @@
         }
 
         private void showVeils(@NonNull SurfaceControl.Transaction t) {
-            t.setColor(mPrimaryVeil, colorToFloatArray(mProperties.mPrimaryVeilColor))
-                    .setColor(mSecondaryVeil, colorToFloatArray(mProperties.mSecondaryVeilColor))
+            final Color primaryVeilColor = getContainerBackgroundColor(
+                    mProperties.mPrimaryContainer, DEFAULT_PRIMARY_VEIL_COLOR);
+            final Color secondaryVeilColor = getContainerBackgroundColor(
+                    mProperties.mSecondaryContainer, DEFAULT_SECONDARY_VEIL_COLOR);
+            t.setColor(mPrimaryVeil, colorToFloatArray(primaryVeilColor))
+                    .setColor(mSecondaryVeil, colorToFloatArray(secondaryVeilColor))
                     .setLayer(mDividerSurface, DIVIDER_LAYER)
                     .setLayer(mPrimaryVeil, VEIL_LAYER)
                     .setLayer(mSecondaryVeil, VEIL_LAYER)
@@ -1347,13 +1354,14 @@
             Rect secondaryBounds;
             if (mProperties.mIsVerticalSplit) {
                 final Rect boundsLeft = new Rect(0, 0, mDividerPosition, taskBounds.height());
-                final Rect boundsRight = new Rect(mDividerPosition + mDividerWidthPx, 0,
+                final Rect boundsRight = new Rect(mDividerPosition + mProperties.mDividerWidthPx, 0,
                         taskBounds.width(), taskBounds.height());
                 primaryBounds = mProperties.mIsReversedLayout ? boundsRight : boundsLeft;
                 secondaryBounds = mProperties.mIsReversedLayout ? boundsLeft : boundsRight;
             } else {
                 final Rect boundsTop = new Rect(0, 0, taskBounds.width(), mDividerPosition);
-                final Rect boundsBottom = new Rect(0, mDividerPosition + mDividerWidthPx,
+                final Rect boundsBottom = new Rect(
+                        0, mDividerPosition + mProperties.mDividerWidthPx,
                         taskBounds.width(), taskBounds.height());
                 primaryBounds = mProperties.mIsReversedLayout ? boundsBottom : boundsTop;
                 secondaryBounds = mProperties.mIsReversedLayout ? boundsTop : boundsBottom;
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 9aa12aa..f78e2b5 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -1322,7 +1322,9 @@
             mPresenter.expandTaskFragment(wct, container);
         } else {
             // Put activity into a new expanded container.
-            final TaskFragmentContainer newContainer = newContainer(activity, getTaskId(activity));
+            final TaskFragmentContainer newContainer =
+                    new TaskFragmentContainer.Builder(this, getTaskId(activity), activity)
+                            .setPendingAppearedActivity(activity).build();
             mPresenter.expandActivity(wct, newContainer.getTaskFragmentToken(), activity);
         }
     }
@@ -1738,9 +1740,13 @@
             // Can't find any activity in the Task that we can use as the owner activity.
             return null;
         }
-        final TaskFragmentContainer container = newContainer(null /* pendingAppearedActivity */,
-                intent, activityInTask, taskId, null /* pairedPrimaryContainer*/, overlayTag,
-                launchOptions, associateLaunchingActivity);
+        final TaskFragmentContainer container =
+                new TaskFragmentContainer.Builder(this, taskId, activityInTask)
+                        .setPendingAppearedIntent(intent)
+                        .setOverlayTag(overlayTag)
+                        .setLaunchOptions(launchOptions)
+                        .setAssociatedActivity(associateLaunchingActivity ? activityInTask : null)
+                        .build();
         final IBinder taskFragmentToken = container.getTaskFragmentToken();
         // Note that taskContainer will not exist before calling #newContainer if the container
         // is the first embedded TF in the task.
@@ -1818,74 +1824,6 @@
         return null;
     }
 
-    @GuardedBy("mLock")
-    TaskFragmentContainer newContainer(@NonNull Activity pendingAppearedActivity, int taskId) {
-        return newContainer(pendingAppearedActivity, pendingAppearedActivity, taskId);
-    }
-
-    @GuardedBy("mLock")
-    TaskFragmentContainer newContainer(@NonNull Activity pendingAppearedActivity,
-            @NonNull Activity activityInTask, int taskId) {
-        return newContainer(pendingAppearedActivity, null /* pendingAppearedIntent */,
-                activityInTask, taskId, null /* pairedPrimaryContainer */, null /* tag */,
-                null /* launchOptions */, false /* associateLaunchingActivity */);
-    }
-
-    @GuardedBy("mLock")
-    TaskFragmentContainer newContainer(@NonNull Intent pendingAppearedIntent,
-            @NonNull Activity activityInTask, int taskId) {
-        return newContainer(null /* pendingAppearedActivity */, pendingAppearedIntent,
-                activityInTask, taskId, null /* pairedPrimaryContainer */, null /* tag */,
-                null /* launchOptions */, false /* associateLaunchingActivity */);
-    }
-
-    @GuardedBy("mLock")
-    TaskFragmentContainer newContainer(@NonNull Intent pendingAppearedIntent,
-            @NonNull Activity activityInTask, int taskId,
-            @NonNull TaskFragmentContainer pairedPrimaryContainer) {
-        return newContainer(null /* pendingAppearedActivity */, pendingAppearedIntent,
-                activityInTask, taskId, pairedPrimaryContainer, null /* tag */,
-                null /* launchOptions */, false /* associateLaunchingActivity */);
-    }
-
-    /**
-     * Creates and registers a new organized container with an optional activity that will be
-     * re-parented to it in a WCT.
-     *
-     * @param pendingAppearedActivity the activity that will be reparented to the TaskFragment.
-     * @param pendingAppearedIntent   the Intent that will be started in the TaskFragment.
-     * @param activityInTask          activity in the same Task so that we can get the Task bounds
-     *                                if needed.
-     * @param taskId                  parent Task of the new TaskFragment.
-     * @param pairedContainer  the paired primary {@link TaskFragmentContainer}. When it is
-     *                                set, the new container will be added right above it.
-     * @param overlayTag              The tag for the new created overlay container. It must be
-     *                                needed if {@code isOverlay} is {@code true}. Otherwise,
-     *                                it should be {@code null}.
-     * @param launchOptions           The launch options bundle to create a container. Must be
-     *                                specified for overlay container.
-     * @param associateLaunchingActivity {@code true} to indicate this overlay container
-     *                                   should associate with launching activity.
-     */
-    @GuardedBy("mLock")
-    TaskFragmentContainer newContainer(@Nullable Activity pendingAppearedActivity,
-            @Nullable Intent pendingAppearedIntent, @NonNull Activity activityInTask, int taskId,
-            @Nullable TaskFragmentContainer pairedContainer, @Nullable String overlayTag,
-            @Nullable Bundle launchOptions, boolean associateLaunchingActivity) {
-        if (activityInTask == null) {
-            throw new IllegalArgumentException("activityInTask must not be null,");
-        }
-        if (!mTaskContainers.contains(taskId)) {
-            mTaskContainers.put(taskId, new TaskContainer(taskId, activityInTask));
-            mDividerPresenters.put(taskId, new DividerPresenter(taskId, this, mExecutor));
-        }
-        final TaskContainer taskContainer = mTaskContainers.get(taskId);
-        final TaskFragmentContainer container = new TaskFragmentContainer(pendingAppearedActivity,
-                pendingAppearedIntent, taskContainer, this, pairedContainer, overlayTag,
-                launchOptions, associateLaunchingActivity ? activityInTask : null);
-        return container;
-    }
-
     /**
      * Creates and registers a new split with the provided containers and configuration. Finishes
      * existing secondary containers if found for the given primary container.
@@ -2581,6 +2519,12 @@
         return mTaskContainers.get(taskId);
     }
 
+    @GuardedBy("mLock")
+    void addTaskContainer(int taskId, TaskContainer taskContainer) {
+        mTaskContainers.put(taskId, taskContainer);
+        mDividerPresenters.put(taskId, new DividerPresenter(taskId, this, mExecutor));
+    }
+
     Handler getHandler() {
         return mHandler;
     }
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 1cb410e..eade86e 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -186,8 +186,9 @@
 
         // Create new empty task fragment
         final int taskId = primaryContainer.getTaskId();
-        final TaskFragmentContainer secondaryContainer = mController.newContainer(
-                secondaryIntent, primaryActivity, taskId);
+        final TaskFragmentContainer secondaryContainer =
+                new TaskFragmentContainer.Builder(mController, taskId, primaryActivity)
+                        .setPendingAppearedIntent(secondaryIntent).build();
         final Rect secondaryRelBounds = getRelBoundsForPosition(POSITION_END, taskProperties,
                 splitAttributes);
         final int windowingMode = mController.getTaskContainer(taskId)
@@ -261,7 +262,8 @@
         TaskFragmentContainer container = mController.getContainerWithActivity(activity);
         final int taskId = container != null ? container.getTaskId() : activity.getTaskId();
         if (container == null || container == containerToAvoid) {
-            container = mController.newContainer(activity, taskId);
+            container = new TaskFragmentContainer.Builder(mController, taskId, activity)
+                    .setPendingAppearedActivity(activity).build();
             final int windowingMode = mController.getTaskContainer(taskId)
                     .getWindowingModeForTaskFragment(relBounds);
             final IBinder reparentActivityToken = activity.getActivityToken();
@@ -304,15 +306,19 @@
         TaskFragmentContainer primaryContainer = mController.getContainerWithActivity(
                 launchingActivity);
         if (primaryContainer == null) {
-            primaryContainer = mController.newContainer(launchingActivity,
-                    launchingActivity.getTaskId());
+            primaryContainer = new TaskFragmentContainer.Builder(mController,
+                    launchingActivity.getTaskId(), launchingActivity)
+                    .setPendingAppearedActivity(launchingActivity).build();
         }
 
         final int taskId = primaryContainer.getTaskId();
-        final TaskFragmentContainer secondaryContainer = mController.newContainer(activityIntent,
-                launchingActivity, taskId,
-                // Pass in the primary container to make sure it is added right above the primary.
-                primaryContainer);
+        final TaskFragmentContainer secondaryContainer =
+                new TaskFragmentContainer.Builder(mController, taskId, launchingActivity)
+                        .setPendingAppearedIntent(activityIntent)
+                        // Pass in the primary container to make sure it is added right above the
+                        // primary.
+                        .setPairedPrimaryContainer(primaryContainer)
+                        .build();
         final TaskContainer taskContainer = mController.getTaskContainer(taskId);
         final int windowingMode = taskContainer.getWindowingModeForTaskFragment(
                 primaryRelBounds);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index c708da9..ee00c4c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -510,7 +510,7 @@
             return;
         }
         final TaskFragmentContainer primaryContainer = topSplitContainer.getPrimaryContainer();
-        final float newRatio = dividerPresenter.calculateNewSplitRatio(topSplitContainer);
+        final float newRatio = dividerPresenter.calculateNewSplitRatio();
 
         // If the primary container is fully expanded, we should finish all the associated
         // secondary containers.
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 d0b6a01..7173b0c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -195,20 +195,6 @@
     private boolean mLastDimOnTask;
 
     /**
-     * @see #TaskFragmentContainer(Activity, Intent, TaskContainer, SplitController,
-     * TaskFragmentContainer, String, Bundle, Activity)
-     */
-    TaskFragmentContainer(@Nullable Activity pendingAppearedActivity,
-                          @Nullable Intent pendingAppearedIntent,
-                          @NonNull TaskContainer taskContainer,
-                          @NonNull SplitController controller,
-                          @Nullable TaskFragmentContainer pairedPrimaryContainer) {
-        this(pendingAppearedActivity, pendingAppearedIntent, taskContainer,
-                controller, pairedPrimaryContainer, null /* overlayTag */,
-                null /* launchOptions */, null /* associatedActivity */);
-    }
-
-    /**
      * Creates a container with an existing activity that will be re-parented to it in a window
      * container transaction.
      * @param pairedPrimaryContainer    when it is set, the new container will be add right above it
@@ -218,7 +204,7 @@
      * @param associatedActivity        the associated activity of the overlay container. Must be
      *                                  {@code null} for a non-overlay container.
      */
-    TaskFragmentContainer(@Nullable Activity pendingAppearedActivity,
+    private TaskFragmentContainer(@Nullable Activity pendingAppearedActivity,
             @Nullable Intent pendingAppearedIntent, @NonNull TaskContainer taskContainer,
             @NonNull SplitController controller,
             @Nullable TaskFragmentContainer pairedPrimaryContainer, @Nullable String overlayTag,
@@ -232,12 +218,6 @@
         mToken = new Binder("TaskFragmentContainer");
         mTaskContainer = taskContainer;
         mOverlayTag = overlayTag;
-        if (overlayTag != null) {
-            Objects.requireNonNull(launchOptions);
-        } else if (associatedActivity != null) {
-            throw new IllegalArgumentException("Associated activity must be null for "
-                    + "non-overlay activity.");
-        }
         mAssociatedActivityToken = associatedActivity != null
                 ? associatedActivity.getActivityToken() : null;
 
@@ -1116,6 +1096,117 @@
         return sb.append("]").toString();
     }
 
+    static final class Builder {
+        @NonNull
+        private final SplitController mSplitController;
+
+        // The parent Task id of the new TaskFragment.
+        private final int mTaskId;
+
+        // The activity in the same Task so that we can get the Task bounds if needed.
+        @NonNull
+        private final Activity mActivityInTask;
+
+        // The activity that will be reparented to the TaskFragment.
+        @Nullable
+        private Activity mPendingAppearedActivity;
+
+        // The Intent that will be started in the TaskFragment.
+        @Nullable
+        private Intent mPendingAppearedIntent;
+
+        // The paired primary {@link TaskFragmentContainer}. When it is set, the new container
+        // will be added right above it.
+        @Nullable
+        private TaskFragmentContainer mPairedPrimaryContainer;
+
+        // The launch options bundle to create a container. Must be specified for overlay container.
+        @Nullable
+        private Bundle mLaunchOptions;
+
+        // The tag for the new created overlay container. This is required when creating an
+        // overlay container.
+        @Nullable
+        private String mOverlayTag;
+
+        // The associated activity of the overlay container. Must be {@code null} for a
+        // non-overlay container.
+        @Nullable
+        private Activity mAssociatedActivity;
+
+        Builder(@NonNull SplitController splitController, int taskId,
+                @Nullable Activity activityInTask) {
+            if (taskId <= 0) {
+                throw new IllegalArgumentException("taskId is invalid, " + taskId);
+            }
+
+            mSplitController = splitController;
+            mTaskId = taskId;
+            mActivityInTask = activityInTask;
+        }
+
+        @NonNull
+        Builder setPendingAppearedActivity(@Nullable Activity pendingAppearedActivity) {
+            mPendingAppearedActivity = pendingAppearedActivity;
+            return this;
+        }
+
+        @NonNull
+        Builder setPendingAppearedIntent(@Nullable Intent pendingAppearedIntent) {
+            mPendingAppearedIntent = pendingAppearedIntent;
+            return this;
+        }
+
+        @NonNull
+        Builder setPairedPrimaryContainer(@Nullable TaskFragmentContainer pairedPrimaryContainer) {
+            mPairedPrimaryContainer = pairedPrimaryContainer;
+            return this;
+        }
+
+        @NonNull
+        Builder setLaunchOptions(@Nullable Bundle launchOptions) {
+            mLaunchOptions = launchOptions;
+            return this;
+        }
+
+        @NonNull
+        Builder setOverlayTag(@Nullable String overlayTag) {
+            mOverlayTag = overlayTag;
+            return this;
+        }
+
+        @NonNull
+        Builder setAssociatedActivity(@Nullable Activity associatedActivity) {
+            mAssociatedActivity = associatedActivity;
+            return this;
+        }
+
+        @NonNull
+        TaskFragmentContainer build() {
+            if (mOverlayTag != null) {
+                Objects.requireNonNull(mLaunchOptions);
+            } else if (mAssociatedActivity != null) {
+                throw new IllegalArgumentException("Associated activity must be null for "
+                        + "non-overlay activity.");
+            }
+
+            TaskContainer taskContainer = mSplitController.getTaskContainer(mTaskId);
+            if (taskContainer == null && mActivityInTask == null) {
+                throw new IllegalArgumentException("mActivityInTask must be set.");
+            }
+
+            if (taskContainer == null) {
+                // Adding a TaskContainer if no existed one.
+                taskContainer = new TaskContainer(mTaskId, mActivityInTask);
+                mSplitController.addTaskContainer(mTaskId, taskContainer);
+            }
+
+            return new TaskFragmentContainer(mPendingAppearedActivity, mPendingAppearedIntent,
+                    taskContainer, mSplitController, mPairedPrimaryContainer, mOverlayTag,
+                    mLaunchOptions, mAssociatedActivity);
+        }
+    }
+
     static class OverlayContainerRestoreParams {
         /** The token of the overlay container */
         @NonNull
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
index 746607c..3f67607 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/DividerPresenterTest.java
@@ -144,6 +144,7 @@
                 new SplitAttributes.Builder()
                         .setDividerAttributes(DEFAULT_DIVIDER_ATTRIBUTES)
                         .build());
+        final Rect mockTaskBounds = new Rect(0, 0, 2000, 1000);
         final TaskFragmentContainer mockPrimaryContainer =
                 createMockTaskFragmentContainer(
                         mPrimaryContainerToken, new Rect(0, 0, 950, 1000));
@@ -158,13 +159,15 @@
                 DEFAULT_DIVIDER_ATTRIBUTES,
                 mSurfaceControl,
                 getInitialDividerPosition(
-                        mSplitContainer, true /* isVerticalSplit */, false /* isReversedLayout */),
+                        mockPrimaryContainer, mockSecondaryContainer, mockTaskBounds,
+                        50 /* divideWidthPx */, false /* isDraggableExpandType */,
+                        true /* isVerticalSplit */, false /* isReversedLayout */),
                 true /* isVerticalSplit */,
                 false /* isReversedLayout */,
                 Display.DEFAULT_DISPLAY,
                 false /* isDraggableExpandType */,
-                Color.valueOf(Color.BLACK), /* primaryVeilColor */
-                Color.valueOf(Color.GRAY) /* secondaryVeilColor */
+                mockPrimaryContainer,
+                mockSecondaryContainer
         );
 
         mDividerPresenter = new DividerPresenter(
@@ -502,7 +505,6 @@
         assertEquals(
                 0.3f, // Primary is 300px after dragging.
                 DividerPresenter.calculateNewSplitRatio(
-                        mSplitContainer,
                         dividerPosition,
                         taskBounds,
                         dividerWidthPx,
@@ -518,7 +520,6 @@
         assertEquals(
                 DividerPresenter.RATIO_EXPANDED_SECONDARY,
                 DividerPresenter.calculateNewSplitRatio(
-                        mSplitContainer,
                         dividerPosition,
                         taskBounds,
                         dividerWidthPx,
@@ -535,7 +536,6 @@
         assertEquals(
                 0.2f, // Adjusted to the minPosition 200
                 DividerPresenter.calculateNewSplitRatio(
-                        mSplitContainer,
                         dividerPosition,
                         taskBounds,
                         dividerWidthPx,
@@ -569,7 +569,6 @@
                 // After dragging, secondary is [0, 0, 2000, 300]. Primary is [0, 400, 2000, 1100].
                 0.7f,
                 DividerPresenter.calculateNewSplitRatio(
-                        mSplitContainer,
                         dividerPosition,
                         taskBounds,
                         dividerWidthPx,
@@ -587,7 +586,6 @@
                 // The primary (bottom) container is expanded
                 DividerPresenter.RATIO_EXPANDED_PRIMARY,
                 DividerPresenter.calculateNewSplitRatio(
-                        mSplitContainer,
                         dividerPosition,
                         taskBounds,
                         dividerWidthPx,
@@ -605,7 +603,6 @@
                 // Adjusted to minPosition 200, so the primary (bottom) container is 800.
                 0.8f,
                 DividerPresenter.calculateNewSplitRatio(
-                        mSplitContainer,
                         dividerPosition,
                         taskBounds,
                         dividerWidthPx,
@@ -720,7 +717,7 @@
 
         // Divider position is greater than minPosition and the velocity is enough for fling
         assertEquals(
-                0, // Closed position
+                30, // minPosition
                 DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
                         50 /* dividerPosition */,
                         30 /* minPosition */,
@@ -731,7 +728,7 @@
 
         // Divider position is less than maxPosition and the velocity is enough for fling
         assertEquals(
-                1200, // Fully expanded position
+                900, // maxPosition
                 DividerPresenter.dividerPositionWithDraggingToFullscreenAllowed(
                         800 /* dividerPosition */,
                         30 /* minPosition */,
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
index a069ac7..d649c6d 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
@@ -248,4 +248,17 @@
         return new SplitPlaceholderRule.Builder(placeholderIntent, activityPredicate,
                 intentPredicate, windowMetricsPredicate);
     }
+
+    @NonNull
+    static TaskFragmentContainer createTfContainer(
+            @NonNull SplitController splitController, @NonNull Activity activity) {
+        return createTfContainer(splitController, TASK_ID, activity);
+    }
+
+    @NonNull
+    static TaskFragmentContainer createTfContainer(
+            @NonNull SplitController splitController, int taskId, @NonNull Activity activity) {
+        return new TaskFragmentContainer.Builder(splitController, taskId, activity)
+                .setPendingAppearedActivity(activity).build();
+    }
 }
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
index 76e6a0f..7b473b0 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
@@ -25,6 +25,7 @@
 
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 
@@ -105,8 +106,11 @@
     @Test
     public void testExpandTaskFragment() {
         final TaskContainer taskContainer = createTestTaskContainer();
-        final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
-                new Intent(), taskContainer, mSplitController, null /* pairedPrimaryContainer */);
+        doReturn(taskContainer).when(mSplitController).getTaskContainer(anyInt());
+        final TaskFragmentContainer container =  new TaskFragmentContainer.Builder(mSplitController,
+                taskContainer.getTaskId(), null /* activityInTask */)
+                .setPendingAppearedIntent(new Intent())
+                .build();
         final TaskFragmentInfo info = createMockInfo(container);
         mOrganizer.mFragmentInfos.put(container.getTaskFragmentToken(), info);
         container.setInfo(mTransaction, info);
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
index 86b7e88..0972d40 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java
@@ -29,6 +29,7 @@
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPairRuleBuilder;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPlaceholderRuleBuilder;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTfContainer;
 import static androidx.window.extensions.embedding.SplitPresenter.sanitizeBounds;
 import static androidx.window.extensions.embedding.WindowAttributes.DIM_AREA_ON_TASK;
 
@@ -530,8 +531,8 @@
 
     @Test
     public void testUpdateActivityStackAttributes_nullContainer_earlyReturn() {
-        final TaskFragmentContainer container = mSplitController.newContainer(mActivity,
-                mActivity.getTaskId());
+        final TaskFragmentContainer container = createTfContainer(mSplitController,
+                mActivity.getTaskId(), mActivity);
         mSplitController.updateActivityStackAttributes(
                 ActivityStack.Token.createFromBinder(container.getTaskFragmentToken()),
                 new ActivityStackAttributes.Builder().build());
@@ -837,8 +838,9 @@
         final Intent intent = new Intent();
         final IBinder fillTaskActivityToken = new Binder();
         final IBinder lastOverlayToken = new Binder();
-        final TaskFragmentContainer overlayContainer = mSplitController.newContainer(intent,
-                mActivity, TASK_ID);
+        final TaskFragmentContainer overlayContainer =
+                new TaskFragmentContainer.Builder(mSplitController, TASK_ID, mActivity)
+                        .setPendingAppearedIntent(intent).build();
         final TaskFragmentContainer.OverlayContainerRestoreParams params = mock(
                 TaskFragmentContainer.OverlayContainerRestoreParams.class);
         doReturn(params).when(mSplitController).getOverlayContainerRestoreParams(any(), any());
@@ -884,8 +886,8 @@
     @NonNull
     private TaskFragmentContainer createMockTaskFragmentContainer(
             @NonNull Activity activity, boolean isVisible) {
-        final TaskFragmentContainer container = mSplitController.newContainer(activity,
-                activity.getTaskId());
+        final TaskFragmentContainer container = createTfContainer(mSplitController,
+                activity.getTaskId(), activity);
         setupTaskFragmentInfo(container, activity, isVisible);
         return container;
     }
@@ -918,10 +920,13 @@
             @Nullable Activity launchingActivity) {
         final Activity activity = launchingActivity != null
                 ? launchingActivity : createMockActivity();
-        TaskFragmentContainer overlayContainer = mSplitController.newContainer(
-                null /* pendingAppearedActivity */, mIntent, activity, taskId,
-                null /* pairedPrimaryContainer */, tag, Bundle.EMPTY,
-                associateLaunchingActivity);
+        TaskFragmentContainer overlayContainer =
+                new TaskFragmentContainer.Builder(mSplitController, taskId, activity)
+                        .setPendingAppearedIntent(mIntent)
+                        .setOverlayTag(tag)
+                        .setLaunchOptions(Bundle.EMPTY)
+                        .setAssociatedActivity(associateLaunchingActivity ? activity : null)
+                        .build();
         setupTaskFragmentInfo(overlayContainer, createMockActivity(), isVisible);
         return overlayContainer;
     }
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index 35353db..640b1fc 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -41,6 +41,7 @@
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPlaceholderRuleBuilder;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTestTaskContainer;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTfContainer;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds;
 import static androidx.window.extensions.embedding.SplitRule.FINISH_ALWAYS;
 
@@ -59,7 +60,6 @@
 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.ArgumentMatchers.isNull;
 import static org.mockito.Mockito.clearInvocations;
@@ -198,7 +198,7 @@
 
     @Test
     public void testOnTaskFragmentVanished() {
-        final TaskFragmentContainer tf = mSplitController.newContainer(mActivity, TASK_ID);
+        final TaskFragmentContainer tf = createTfContainer(mSplitController, mActivity);
         doReturn(tf.getTaskFragmentToken()).when(mInfo).getFragmentToken();
 
         // The TaskFragment has been removed in the server, we only need to cleanup the reference.
@@ -213,7 +213,7 @@
     public void testOnTaskFragmentAppearEmptyTimeout() {
         // Setup to make sure a transaction record is started.
         mTransactionManager.startNewTransaction();
-        final TaskFragmentContainer tf = mSplitController.newContainer(mActivity, TASK_ID);
+        final TaskFragmentContainer tf = createTfContainer(mSplitController, mActivity);
         doCallRealMethod().when(mSplitController).onTaskFragmentAppearEmptyTimeout(any(), any());
         mSplitController.onTaskFragmentAppearEmptyTimeout(mTransaction, tf);
 
@@ -224,7 +224,7 @@
     @Test
     public void testOnActivityDestroyed() {
         doReturn(new Binder()).when(mActivity).getActivityToken();
-        final TaskFragmentContainer tf = mSplitController.newContainer(mActivity, TASK_ID);
+        final TaskFragmentContainer tf = createTfContainer(mSplitController, mActivity);
 
         assertTrue(tf.hasActivity(mActivity.getActivityToken()));
 
@@ -245,12 +245,9 @@
     public void testNewContainer() {
         // Must pass in a valid activity.
         assertThrows(IllegalArgumentException.class, () ->
-                mSplitController.newContainer(null /* activity */, TASK_ID));
-        assertThrows(IllegalArgumentException.class, () ->
-                mSplitController.newContainer(mActivity, null /* launchingActivity */, TASK_ID));
+                createTfContainer(mSplitController, null /* activity */));
 
-        final TaskFragmentContainer tf = mSplitController.newContainer(mActivity, mActivity,
-                TASK_ID);
+        final TaskFragmentContainer tf = createTfContainer(mSplitController, mActivity);
         final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID);
 
         assertNotNull(tf);
@@ -263,7 +260,7 @@
     public void testUpdateContainer() {
         // Make SplitController#launchPlaceholderIfNecessary(TaskFragmentContainer) return true
         // and verify if shouldContainerBeExpanded() not called.
-        final TaskFragmentContainer tf = mSplitController.newContainer(mActivity, TASK_ID);
+        final TaskFragmentContainer tf = createTfContainer(mSplitController, mActivity);
         spyOn(tf);
         doReturn(mActivity).when(tf).getTopNonFinishingActivity();
         doReturn(true).when(tf).isEmpty();
@@ -369,8 +366,12 @@
     public void testOnStartActivityResultError() {
         final Intent intent = new Intent();
         final TaskContainer taskContainer = createTestTaskContainer();
-        final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
-                intent, taskContainer, mSplitController, null /* pairedPrimaryContainer */);
+        final int taskId = taskContainer.getTaskId();
+        mSplitController.addTaskContainer(taskId, taskContainer);
+        final TaskFragmentContainer container = new TaskFragmentContainer.Builder(mSplitController,
+                taskId, null /* activityInTask */)
+                .setPendingAppearedIntent(intent)
+                .build();
         final SplitController.ActivityStartMonitor monitor =
                 mSplitController.getActivityStartMonitor();
 
@@ -410,7 +411,8 @@
     @Test
     public void testOnActivityReparentedToTask_diffProcess() {
         // Create an empty TaskFragment to initialize for the Task.
-        mSplitController.newContainer(new Intent(), mActivity, TASK_ID);
+        new TaskFragmentContainer.Builder(mSplitController, TASK_ID, mActivity)
+                .setPendingAppearedIntent(new Intent()).build();
         final IBinder activityToken = new Binder();
         final Intent intent = new Intent();
 
@@ -595,8 +597,9 @@
         verify(mTransaction, never()).reparentActivityToTaskFragment(any(), any());
 
         // Place in the top container if there is no other rule matched.
-        final TaskFragmentContainer topContainer = mSplitController
-                .newContainer(new Intent(), mActivity, TASK_ID);
+        final TaskFragmentContainer topContainer =
+                new TaskFragmentContainer.Builder(mSplitController, TASK_ID, mActivity)
+                        .setPendingAppearedIntent(new Intent()).build();
         mSplitController.placeActivityInTopContainer(mTransaction, mActivity);
 
         verify(mTransaction).reparentActivityToTaskFragment(topContainer.getTaskFragmentToken(),
@@ -604,7 +607,7 @@
 
         // Not reparent if activity is in a TaskFragment.
         clearInvocations(mTransaction);
-        mSplitController.newContainer(mActivity, TASK_ID);
+        createTfContainer(mSplitController, mActivity);
         mSplitController.placeActivityInTopContainer(mTransaction, mActivity);
 
         verify(mTransaction, never()).reparentActivityToTaskFragment(any(), any());
@@ -616,8 +619,7 @@
                 false /* isOnReparent */);
 
         assertFalse(result);
-        verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any(),
-                anyString(), any(), anyBoolean());
+        verify(mSplitController, never()).addTaskContainer(anyInt(), any());
     }
 
     @Test
@@ -632,7 +634,6 @@
 
         assertTrue(result);
         assertNotNull(container);
-        verify(mSplitController).newContainer(mActivity, TASK_ID);
         verify(mSplitPresenter).expandActivity(mTransaction, container.getTaskFragmentToken(),
                 mActivity);
     }
@@ -642,7 +643,7 @@
         setupExpandRule(mActivity);
 
         // When the activity is not in any TaskFragment, create a new expanded TaskFragment for it.
-        final TaskFragmentContainer container = mSplitController.newContainer(mActivity, TASK_ID);
+        final TaskFragmentContainer container = createTfContainer(mSplitController, mActivity);
         final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
                 false /* isOnReparent */);
 
@@ -692,8 +693,8 @@
 
         // Don't launch placeholder if the activity is not in the topmost active TaskFragment.
         final Activity activity = createMockActivity();
-        mSplitController.newContainer(mActivity, TASK_ID);
-        mSplitController.newContainer(activity, TASK_ID);
+        createTfContainer(mSplitController, mActivity);
+        createTfContainer(mSplitController, activity);
         final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
                 false /* isOnReparent */);
 
@@ -711,7 +712,7 @@
                 (SplitPlaceholderRule) mSplitController.getSplitRules().get(0);
 
         // Launch placeholder if the activity is in the topmost expanded TaskFragment.
-        mSplitController.newContainer(mActivity, TASK_ID);
+        createTfContainer(mSplitController, mActivity);
         final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
                 false /* isOnReparent */);
 
@@ -763,10 +764,11 @@
         final SplitPairRule splitRule = (SplitPairRule) mSplitController.getSplitRules().get(0);
 
         // Activity is already in primary split, no need to create new split.
-        final TaskFragmentContainer primaryContainer = mSplitController.newContainer(mActivity,
-                TASK_ID);
-        final TaskFragmentContainer secondaryContainer = mSplitController.newContainer(
-                secondaryIntent, mActivity, TASK_ID);
+        final TaskFragmentContainer primaryContainer =
+                createTfContainer(mSplitController, mActivity);
+        final TaskFragmentContainer secondaryContainer =
+                new TaskFragmentContainer.Builder(mSplitController, TASK_ID, mActivity)
+                        .setPendingAppearedIntent(secondaryIntent).build();
         mSplitController.registerSplit(
                 mTransaction,
                 primaryContainer,
@@ -779,8 +781,6 @@
                 false /* isOnReparent */);
 
         assertTrue(result);
-        verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any(),
-                anyString(), any(), anyBoolean());
         verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any(), any());
     }
 
@@ -792,10 +792,11 @@
 
         // The new launched activity is in primary split, but there is no rule for it to split with
         // the secondary, so return false.
-        final TaskFragmentContainer primaryContainer = mSplitController.newContainer(mActivity,
-                TASK_ID);
-        final TaskFragmentContainer secondaryContainer = mSplitController.newContainer(
-                secondaryIntent, mActivity, TASK_ID);
+        final TaskFragmentContainer primaryContainer =
+                createTfContainer(mSplitController, mActivity);
+        final TaskFragmentContainer secondaryContainer =
+                new TaskFragmentContainer.Builder(mSplitController, TASK_ID, mActivity)
+                        .setPendingAppearedIntent(secondaryIntent).build();
         mSplitController.registerSplit(
                 mTransaction,
                 primaryContainer,
@@ -822,8 +823,6 @@
                 false /* isOnReparent */);
 
         assertTrue(result);
-        verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt(), any(),
-                anyString(), any(), anyBoolean());
         verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any(), any());
     }
 
@@ -852,10 +851,10 @@
         doReturn(PLACEHOLDER_INTENT).when(mActivity).getIntent();
 
         // Activity is a placeholder.
-        final TaskFragmentContainer primaryContainer = mSplitController.newContainer(
-                primaryActivity, TASK_ID);
-        final TaskFragmentContainer secondaryContainer = mSplitController.newContainer(mActivity,
-                TASK_ID);
+        final TaskFragmentContainer primaryContainer =
+                createTfContainer(mSplitController, primaryActivity);
+        final TaskFragmentContainer secondaryContainer =
+                createTfContainer(mSplitController, mActivity);
         mSplitController.registerSplit(
                 mTransaction,
                 primaryContainer,
@@ -874,8 +873,7 @@
         final Activity activityBelow = createMockActivity();
         setupSplitRule(activityBelow, mActivity);
 
-        final TaskFragmentContainer container = mSplitController.newContainer(activityBelow,
-                TASK_ID);
+        final TaskFragmentContainer container = createTfContainer(mSplitController, activityBelow);
         container.addPendingAppearedActivity(mActivity);
         final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
                 false /* isOnReparent */);
@@ -890,8 +888,7 @@
         setupSplitRule(mActivity, activityBelow);
 
         // Disallow to split as primary.
-        final TaskFragmentContainer container = mSplitController.newContainer(activityBelow,
-                TASK_ID);
+        final TaskFragmentContainer container = createTfContainer(mSplitController, activityBelow);
         container.addPendingAppearedActivity(mActivity);
         boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
                 false /* isOnReparent */);
@@ -961,8 +958,7 @@
 
         doReturn(createActivityInfoWithMinDimensions()).when(mActivity).getActivityInfo();
 
-        final TaskFragmentContainer container = mSplitController.newContainer(activityBelow,
-                TASK_ID);
+        final TaskFragmentContainer container = createTfContainer(mSplitController, activityBelow);
         container.addPendingAppearedActivity(mActivity);
 
         // Allow to split as primary.
@@ -980,8 +976,7 @@
 
         doReturn(createActivityInfoWithMinDimensions()).when(mActivity).getActivityInfo();
 
-        final TaskFragmentContainer container = mSplitController.newContainer(activityBelow,
-                TASK_ID);
+        final TaskFragmentContainer container = createTfContainer(mSplitController, activityBelow);
         container.addPendingAppearedActivity(mActivity);
 
         boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
@@ -1044,8 +1039,8 @@
     public void testResolveActivityToContainer_skipIfNonTopOrPinned() {
         final TaskFragmentContainer container = createMockTaskFragmentContainer(mActivity);
         final Activity pinnedActivity = createMockActivity();
-        final TaskFragmentContainer topContainer = mSplitController.newContainer(pinnedActivity,
-                TASK_ID);
+        final TaskFragmentContainer topContainer =
+                createTfContainer(mSplitController, pinnedActivity);
         final TaskContainer taskContainer = container.getTaskContainer();
         spyOn(taskContainer);
         doReturn(container).when(taskContainer).getTopNonFinishingTaskFragmentContainer(false);
@@ -1351,7 +1346,7 @@
         // Launch placeholder for activity in top TaskFragment.
         setupPlaceholderRule(mActivity);
         mTransactionManager.startNewTransaction();
-        final TaskFragmentContainer container = mSplitController.newContainer(mActivity, TASK_ID);
+        final TaskFragmentContainer container = createTfContainer(mSplitController, mActivity);
         mSplitController.launchPlaceholderIfNecessary(mTransaction, mActivity,
                 true /* isOnCreated */);
 
@@ -1365,9 +1360,10 @@
         // Do not launch placeholder for invisible activity below the top TaskFragment.
         setupPlaceholderRule(mActivity);
         mTransactionManager.startNewTransaction();
-        final TaskFragmentContainer bottomTf = mSplitController.newContainer(mActivity, TASK_ID);
-        final TaskFragmentContainer topTf = mSplitController.newContainer(new Intent(), mActivity,
-                TASK_ID);
+        final TaskFragmentContainer bottomTf = createTfContainer(mSplitController, mActivity);
+        final TaskFragmentContainer topTf =
+                new TaskFragmentContainer.Builder(mSplitController, TASK_ID, mActivity)
+                        .setPendingAppearedIntent(new Intent()).build();
         bottomTf.setInfo(mTransaction, createMockTaskFragmentInfo(bottomTf, mActivity,
                 false /* isVisible */));
         topTf.setInfo(mTransaction, createMockTaskFragmentInfo(topTf, createMockActivity()));
@@ -1383,9 +1379,10 @@
         // Launch placeholder for visible activity below the top TaskFragment.
         setupPlaceholderRule(mActivity);
         mTransactionManager.startNewTransaction();
-        final TaskFragmentContainer bottomTf = mSplitController.newContainer(mActivity, TASK_ID);
-        final TaskFragmentContainer topTf = mSplitController.newContainer(new Intent(), mActivity,
-                TASK_ID);
+        final TaskFragmentContainer bottomTf = createTfContainer(mSplitController, mActivity);
+        final TaskFragmentContainer topTf =
+                new TaskFragmentContainer.Builder(mSplitController, TASK_ID, mActivity)
+                        .setPendingAppearedIntent(new Intent()).build();
         bottomTf.setInfo(mTransaction, createMockTaskFragmentInfo(bottomTf, mActivity,
                 true /* isVisible */));
         topTf.setInfo(mTransaction, createMockTaskFragmentInfo(topTf, createMockActivity()));
@@ -1412,7 +1409,7 @@
 
     @Test
     public void testFinishActivityStacks_finishSingleActivityStack() {
-        TaskFragmentContainer tf = mSplitController.newContainer(mActivity, TASK_ID);
+        TaskFragmentContainer tf = createTfContainer(mSplitController, mActivity);
         tf.setInfo(mTransaction, createMockTaskFragmentInfo(tf, mActivity));
 
         final TaskContainer taskContainer = mSplitController.mTaskContainers.get(TASK_ID);
@@ -1426,8 +1423,8 @@
 
     @Test
     public void testFinishActivityStacks_finishActivityStacksInOrder() {
-        TaskFragmentContainer bottomTf = mSplitController.newContainer(mActivity, TASK_ID);
-        TaskFragmentContainer topTf = mSplitController.newContainer(mActivity, TASK_ID);
+        TaskFragmentContainer bottomTf = createTfContainer(mSplitController, mActivity);
+        TaskFragmentContainer topTf = createTfContainer(mSplitController, mActivity);
         bottomTf.setInfo(mTransaction, createMockTaskFragmentInfo(bottomTf, mActivity));
         topTf.setInfo(mTransaction, createMockTaskFragmentInfo(topTf, createMockActivity()));
 
@@ -1687,8 +1684,8 @@
 
     /** Creates a mock TaskFragment that has been registered and appeared in the organizer. */
     private TaskFragmentContainer createMockTaskFragmentContainer(@NonNull Activity activity) {
-        final TaskFragmentContainer container = mSplitController.newContainer(activity,
-                activity.getTaskId());
+        final TaskFragmentContainer container = createTfContainer(mSplitController,
+                activity.getTaskId(), activity);
         setupTaskFragmentInfo(container, activity);
         return container;
     }
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
index 3fbce9ec..816e2da 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
@@ -31,6 +31,7 @@
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitPairRuleBuilder;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTfContainer;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.createWindowLayoutInfo;
 import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds;
 import static androidx.window.extensions.embedding.SplitPresenter.EXPAND_CONTAINERS_ATTRIBUTES;
@@ -139,7 +140,7 @@
 
     @Test
     public void testCreateTaskFragment() {
-        final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID);
+        final TaskFragmentContainer container = createTfContainer(mController, mActivity);
         mPresenter.createTaskFragment(mTransaction, container.getTaskFragmentToken(),
                 mActivity.getActivityToken(), TASK_BOUNDS, WINDOWING_MODE_MULTI_WINDOW);
 
@@ -150,7 +151,7 @@
 
     @Test
     public void testResizeTaskFragment() {
-        final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID);
+        final TaskFragmentContainer container = createTfContainer(mController, mActivity);
         mPresenter.mFragmentInfos.put(container.getTaskFragmentToken(), mTaskFragmentInfo);
         mPresenter.resizeTaskFragment(mTransaction, container.getTaskFragmentToken(), TASK_BOUNDS);
 
@@ -166,7 +167,7 @@
 
     @Test
     public void testUpdateWindowingMode() {
-        final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID);
+        final TaskFragmentContainer container = createTfContainer(mController, mActivity);
         mPresenter.mFragmentInfos.put(container.getTaskFragmentToken(), mTaskFragmentInfo);
         mPresenter.updateWindowingMode(mTransaction, container.getTaskFragmentToken(),
                 WINDOWING_MODE_MULTI_WINDOW);
@@ -184,8 +185,8 @@
 
     @Test
     public void testSetAdjacentTaskFragments() {
-        final TaskFragmentContainer container0 = mController.newContainer(mActivity, TASK_ID);
-        final TaskFragmentContainer container1 = mController.newContainer(mActivity, TASK_ID);
+        final TaskFragmentContainer container0 = createTfContainer(mController, mActivity);
+        final TaskFragmentContainer container1 = createTfContainer(mController, mActivity);
 
         mPresenter.setAdjacentTaskFragments(mTransaction, container0.getTaskFragmentToken(),
                 container1.getTaskFragmentToken(), null /* adjacentParams */);
@@ -202,8 +203,8 @@
 
     @Test
     public void testClearAdjacentTaskFragments() {
-        final TaskFragmentContainer container0 = mController.newContainer(mActivity, TASK_ID);
-        final TaskFragmentContainer container1 = mController.newContainer(mActivity, TASK_ID);
+        final TaskFragmentContainer container0 = createTfContainer(mController, mActivity);
+        final TaskFragmentContainer container1 = createTfContainer(mController, mActivity);
 
         // No request to clear as it is not set by default.
         mPresenter.clearAdjacentTaskFragments(mTransaction, container0.getTaskFragmentToken());
@@ -224,8 +225,8 @@
 
     @Test
     public void testSetCompanionTaskFragment() {
-        final TaskFragmentContainer container0 = mController.newContainer(mActivity, TASK_ID);
-        final TaskFragmentContainer container1 = mController.newContainer(mActivity, TASK_ID);
+        final TaskFragmentContainer container0 = createTfContainer(mController, mActivity);
+        final TaskFragmentContainer container1 = createTfContainer(mController, mActivity);
 
         mPresenter.setCompanionTaskFragment(mTransaction, container0.getTaskFragmentToken(),
                 container1.getTaskFragmentToken());
@@ -242,7 +243,7 @@
 
     @Test
     public void testSetTaskFragmentDimOnTask() {
-        final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID);
+        final TaskFragmentContainer container = createTfContainer(mController, mActivity);
 
         mPresenter.setTaskFragmentDimOnTask(mTransaction, container.getTaskFragmentToken(), true);
         verify(mTransaction).addTaskFragmentOperation(eq(container.getTaskFragmentToken()), any());
@@ -255,7 +256,7 @@
 
     @Test
     public void testUpdateAnimationParams() {
-        final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID);
+        final TaskFragmentContainer container = createTfContainer(mController, mActivity);
 
         // Verify the default.
         assertTrue(container.areLastRequestedAnimationParamsEqual(
@@ -287,7 +288,7 @@
 
     @Test
     public void testSetTaskFragmentPinned() {
-        final TaskFragmentContainer container = mController.newContainer(mActivity, TASK_ID);
+        final TaskFragmentContainer container = createTfContainer(mController, mActivity);
 
         // Verify the default.
         assertFalse(container.isPinned());
@@ -667,8 +668,8 @@
     public void testExpandSplitContainerIfNeeded() {
         Activity secondaryActivity = createMockActivity();
         SplitRule splitRule = createSplitRule(mActivity, secondaryActivity);
-        TaskFragmentContainer primaryTf = mController.newContainer(mActivity, TASK_ID);
-        TaskFragmentContainer secondaryTf = mController.newContainer(secondaryActivity, TASK_ID);
+        TaskFragmentContainer primaryTf = createTfContainer(mController, mActivity);
+        TaskFragmentContainer secondaryTf = createTfContainer(mController, secondaryActivity);
         SplitContainer splitContainer = new SplitContainer(primaryTf, secondaryActivity,
                 secondaryTf, splitRule, SPLIT_ATTRIBUTES);
 
@@ -710,8 +711,8 @@
     @Test
     public void testCreateNewSplitContainer_secondaryAbovePrimary() {
         final Activity secondaryActivity = createMockActivity();
-        final TaskFragmentContainer bottomTf = mController.newContainer(secondaryActivity, TASK_ID);
-        final TaskFragmentContainer primaryTf = mController.newContainer(mActivity, TASK_ID);
+        final TaskFragmentContainer bottomTf = createTfContainer(mController, secondaryActivity);
+        final TaskFragmentContainer primaryTf = createTfContainer(mController, mActivity);
         final SplitPairRule rule = createSplitPairRuleBuilder(pair ->
                 pair.first == mActivity && pair.second == secondaryActivity, pair -> false,
                 metrics -> true)
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
index 8913b22..2847232 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
@@ -29,6 +29,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 
@@ -57,6 +58,9 @@
  * Build/Install/Run:
  *  atest WMJetpackUnitTests:TaskContainerTest
  */
+
+// Suppress GuardedBy warning on unit tests
+@SuppressWarnings("GuardedBy")
 @Presubmit
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -126,8 +130,11 @@
 
         assertTrue(taskContainer.isEmpty());
 
-        final TaskFragmentContainer tf = new TaskFragmentContainer(null /* activity */,
-                new Intent(), taskContainer, mController, null /* pairedPrimaryContainer */);
+        doReturn(taskContainer).when(mController).getTaskContainer(anyInt());
+        final TaskFragmentContainer tf = new TaskFragmentContainer.Builder(mController,
+                taskContainer.getTaskId(), null /* activityInTask */)
+                .setPendingAppearedIntent(new Intent())
+                .build();
 
         assertFalse(taskContainer.isEmpty());
 
@@ -142,12 +149,17 @@
         final TaskContainer taskContainer = createTestTaskContainer();
         assertNull(taskContainer.getTopNonFinishingTaskFragmentContainer());
 
-        final TaskFragmentContainer tf0 = new TaskFragmentContainer(null /* activity */,
-                new Intent(), taskContainer, mController, null /* pairedPrimaryContainer */);
+        doReturn(taskContainer).when(mController).getTaskContainer(anyInt());
+        final TaskFragmentContainer tf0 = new TaskFragmentContainer.Builder(mController,
+                taskContainer.getTaskId(), null /* activityInTask */)
+                        .setPendingAppearedIntent(new Intent())
+                        .build();
         assertEquals(tf0, taskContainer.getTopNonFinishingTaskFragmentContainer());
 
-        final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */,
-                new Intent(), taskContainer, mController, null /* pairedPrimaryContainer */);
+        final TaskFragmentContainer tf1 = new TaskFragmentContainer.Builder(mController,
+                taskContainer.getTaskId(), null /* activityInTask */)
+                        .setPendingAppearedIntent(new Intent())
+                        .build();
         assertEquals(tf1, taskContainer.getTopNonFinishingTaskFragmentContainer());
     }
 
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
index 44ab2c4..7fab371 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
@@ -100,24 +100,27 @@
     @Test
     public void testNewContainer() {
         final TaskContainer taskContainer = createTestTaskContainer();
+        mController.addTaskContainer(taskContainer.getTaskId(), taskContainer);
 
         // One of the activity and the intent must be non-null
         assertThrows(IllegalArgumentException.class,
-                () -> new TaskFragmentContainer(null, null, taskContainer, mController,
-                        null /* pairedPrimaryContainer */));
+                () -> new TaskFragmentContainer.Builder(mController, taskContainer.getTaskId(),
+                        null /* activityInTask */).build());
 
         // One of the activity and the intent must be null.
         assertThrows(IllegalArgumentException.class,
-                () -> new TaskFragmentContainer(mActivity, mIntent, taskContainer, mController,
-                        null /* pairedPrimaryContainer */));
+                () -> new TaskFragmentContainer.Builder(mController, taskContainer.getTaskId(),
+                        null /* activityInTask */)
+                        .setPendingAppearedActivity(createMockActivity())
+                        .setPendingAppearedIntent(mIntent)
+                        .build());
     }
 
     @Test
     public void testFinish() {
         final TaskContainer taskContainer = createTestTaskContainer();
-        final TaskFragmentContainer container = new TaskFragmentContainer(mActivity,
-                null /* pendingAppearedIntent */, taskContainer, mController,
-                null /* pairedPrimaryContainer */);
+        final TaskFragmentContainer container = createTaskFragmentContainer(taskContainer,
+                mActivity, null /* pendingAppearedIntent */);
         doReturn(container).when(mController).getContainerWithActivity(mActivity);
 
         // Only remove the activity, but not clear the reference until appeared.
@@ -148,15 +151,13 @@
     @Test
     public void testFinish_notFinishActivityThatIsReparenting() {
         final TaskContainer taskContainer = createTestTaskContainer();
-        final TaskFragmentContainer container0 = new TaskFragmentContainer(mActivity,
-                null /* pendingAppearedIntent */, taskContainer, mController,
-                null /* pairedPrimaryContainer */);
+        final TaskFragmentContainer container0 = createTaskFragmentContainer(taskContainer,
+                mActivity, null /* pendingAppearedIntent */);
         final TaskFragmentInfo info = createMockTaskFragmentInfo(container0, mActivity);
         container0.setInfo(mTransaction, info);
         // Request to reparent the activity to a new TaskFragment.
-        final TaskFragmentContainer container1 = new TaskFragmentContainer(mActivity,
-                null /* pendingAppearedIntent */, taskContainer, mController,
-                null /* pairedPrimaryContainer */);
+        final TaskFragmentContainer container1 = createTaskFragmentContainer(taskContainer,
+                mActivity, null /* pendingAppearedIntent */);
         doReturn(container1).when(mController).getContainerWithActivity(mActivity);
 
         // The activity is requested to be reparented, so don't finish it.
@@ -171,15 +172,13 @@
     public void testFinish_alwaysFinishPlaceholder() {
         // Register container1 as a placeholder
         final TaskContainer taskContainer = createTestTaskContainer();
-        final TaskFragmentContainer container0 = new TaskFragmentContainer(mActivity,
-                null /* pendingAppearedIntent */, taskContainer, mController,
-                null /* pairedPrimaryContainer */);
+        final TaskFragmentContainer container0 = createTaskFragmentContainer(taskContainer,
+                mActivity, null /* pendingAppearedIntent */);
         final TaskFragmentInfo info0 = createMockTaskFragmentInfo(container0, mActivity);
         container0.setInfo(mTransaction, info0);
         final Activity placeholderActivity = createMockActivity();
-        final TaskFragmentContainer container1 = new TaskFragmentContainer(placeholderActivity,
-                null /* pendingAppearedIntent */, taskContainer, mController,
-                null /* pairedPrimaryContainer */);
+        final TaskFragmentContainer container1 = createTaskFragmentContainer(taskContainer,
+                placeholderActivity, null /* pendingAppearedIntent */);
         final TaskFragmentInfo info1 = createMockTaskFragmentInfo(container1, placeholderActivity);
         container1.setInfo(mTransaction, info1);
         final SplitAttributes splitAttributes = new SplitAttributes.Builder().build();
@@ -207,9 +206,8 @@
     public void testSetInfo() {
         final TaskContainer taskContainer = createTestTaskContainer();
         // Pending activity should be cleared when it has appeared on server side.
-        final TaskFragmentContainer pendingActivityContainer = new TaskFragmentContainer(mActivity,
-                null /* pendingAppearedIntent */, taskContainer, mController,
-                null /* pairedPrimaryContainer */);
+        final TaskFragmentContainer pendingActivityContainer = createTaskFragmentContainer(
+                taskContainer, mActivity, null /* pendingAppearedIntent */);
 
         assertTrue(pendingActivityContainer.mPendingAppearedActivities.contains(
                 mActivity.getActivityToken()));
@@ -221,9 +219,8 @@
         assertTrue(pendingActivityContainer.mPendingAppearedActivities.isEmpty());
 
         // Pending intent should be cleared when the container becomes non-empty.
-        final TaskFragmentContainer pendingIntentContainer = new TaskFragmentContainer(
-                null /* pendingAppearedActivity */, mIntent, taskContainer, mController,
-                null /* pairedPrimaryContainer */);
+        final TaskFragmentContainer pendingIntentContainer = createTaskFragmentContainer(
+                taskContainer, null /* pendingAppearedActivity */, mIntent);
 
         assertEquals(mIntent, pendingIntentContainer.getPendingAppearedIntent());
 
@@ -237,8 +234,8 @@
     @Test
     public void testIsWaitingActivityAppear() {
         final TaskContainer taskContainer = createTestTaskContainer();
-        final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
-                mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+        final TaskFragmentContainer container = createTaskFragmentContainer(
+                taskContainer, null  /* pendingAppearedActivity */, mIntent);
 
         assertTrue(container.isWaitingActivityAppear());
 
@@ -259,8 +256,8 @@
     public void testAppearEmptyTimeout() {
         doNothing().when(mController).onTaskFragmentAppearEmptyTimeout(any(), any());
         final TaskContainer taskContainer = createTestTaskContainer();
-        final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
-                mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+        final TaskFragmentContainer container = createTaskFragmentContainer(
+                taskContainer, null /* pendingAppearedActivity */, mIntent);
 
         assertNull(container.mAppearEmptyTimeout);
 
@@ -299,8 +296,8 @@
     @Test
     public void testCollectNonFinishingActivities() {
         final TaskContainer taskContainer = createTestTaskContainer();
-        final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
-                mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+        final TaskFragmentContainer container = createTaskFragmentContainer(
+                taskContainer, null /* pendingAppearedActivity */, mIntent);
         List<Activity> activities = container.collectNonFinishingActivities();
 
         assertTrue(activities.isEmpty());
@@ -327,8 +324,8 @@
     @Test
     public void testCollectNonFinishingActivities_checkIfStable() {
         final TaskContainer taskContainer = createTestTaskContainer();
-        final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
-                mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+        final TaskFragmentContainer container = createTaskFragmentContainer(
+                taskContainer, null /* pendingAppearedActivity */, mIntent);
 
         // In case mInfo is null, collectNonFinishingActivities(true) should return null.
         List<Activity> activities =
@@ -353,8 +350,8 @@
     @Test
     public void testAddPendingActivity() {
         final TaskContainer taskContainer = createTestTaskContainer();
-        final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
-                mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+        final TaskFragmentContainer container = createTaskFragmentContainer(
+                taskContainer, null /* pendingAppearedActivity */, mIntent);
         container.addPendingAppearedActivity(mActivity);
 
         assertEquals(1, container.collectNonFinishingActivities().size());
@@ -367,10 +364,10 @@
     @Test
     public void testIsAbove() {
         final TaskContainer taskContainer = createTestTaskContainer();
-        final TaskFragmentContainer container0 = new TaskFragmentContainer(null /* activity */,
-                mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
-        final TaskFragmentContainer container1 = new TaskFragmentContainer(null /* activity */,
-                mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+        final TaskFragmentContainer container0 = createTaskFragmentContainer(
+                taskContainer, null /* pendingAppearedActivity */, mIntent);
+        final TaskFragmentContainer container1 = createTaskFragmentContainer(
+                taskContainer, null /* pendingAppearedActivity */, mIntent);
 
         assertTrue(container1.isAbove(container0));
         assertFalse(container0.isAbove(container1));
@@ -379,8 +376,8 @@
     @Test
     public void testGetBottomMostActivity() {
         final TaskContainer taskContainer = createTestTaskContainer();
-        final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
-                mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+        final TaskFragmentContainer container = createTaskFragmentContainer(
+                taskContainer, null /* pendingAppearedActivity */, mIntent);
         container.addPendingAppearedActivity(mActivity);
 
         assertEquals(mActivity, container.getBottomMostActivity());
@@ -396,8 +393,8 @@
     @Test
     public void testOnActivityDestroyed() {
         final TaskContainer taskContainer = createTestTaskContainer(mController);
-        final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
-                mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+        final TaskFragmentContainer container = createTaskFragmentContainer(
+                taskContainer, null /* pendingAppearedActivity */, mIntent);
         container.addPendingAppearedActivity(mActivity);
         final List<IBinder> activities = new ArrayList<>();
         activities.add(mActivity.getActivityToken());
@@ -416,8 +413,8 @@
     public void testIsInIntermediateState() {
         // True if no info set.
         final TaskContainer taskContainer = createTestTaskContainer();
-        final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
-                mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+        final TaskFragmentContainer container = createTaskFragmentContainer(
+                taskContainer, null /* pendingAppearedActivity */, mIntent);
         spyOn(taskContainer);
         doReturn(true).when(taskContainer).isVisible();
 
@@ -479,8 +476,8 @@
     @Test
     public void testHasAppearedActivity() {
         final TaskContainer taskContainer = createTestTaskContainer();
-        final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
-                mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+        final TaskFragmentContainer container = createTaskFragmentContainer(
+                taskContainer, null /* pendingAppearedActivity */, mIntent);
         container.addPendingAppearedActivity(mActivity);
 
         assertFalse(container.hasAppearedActivity(mActivity.getActivityToken()));
@@ -496,8 +493,8 @@
     @Test
     public void testHasPendingAppearedActivity() {
         final TaskContainer taskContainer = createTestTaskContainer();
-        final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
-                mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+        final TaskFragmentContainer container = createTaskFragmentContainer(
+                taskContainer, null /* pendingAppearedActivity */, mIntent);
         container.addPendingAppearedActivity(mActivity);
 
         assertTrue(container.hasPendingAppearedActivity(mActivity.getActivityToken()));
@@ -513,10 +510,10 @@
     @Test
     public void testHasActivity() {
         final TaskContainer taskContainer = createTestTaskContainer(mController);
-        final TaskFragmentContainer container1 = new TaskFragmentContainer(null /* activity */,
-                mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
-        final TaskFragmentContainer container2 = new TaskFragmentContainer(null /* activity */,
-                mIntent, taskContainer, mController, null /* pairedPrimaryContainer */);
+        final TaskFragmentContainer container1 = createTaskFragmentContainer(
+                taskContainer, null /* pendingAppearedActivity */, mIntent);
+        final TaskFragmentContainer container2 = createTaskFragmentContainer(
+                taskContainer, null /* pendingAppearedActivity */, mIntent);
 
         // Activity is pending appeared on container2.
         container2.addPendingAppearedActivity(mActivity);
@@ -550,17 +547,19 @@
     @Test
     public void testNewContainerWithPairedPrimaryContainer() {
         final TaskContainer taskContainer = createTestTaskContainer();
-        final TaskFragmentContainer tf0 = new TaskFragmentContainer(
-                null /* pendingAppearedActivity */, new Intent(), taskContainer, mController,
-                null /* pairedPrimaryTaskFragment */);
-        final TaskFragmentContainer tf1 = new TaskFragmentContainer(
-                null /* pendingAppearedActivity */, new Intent(), taskContainer, mController,
-                null /* pairedPrimaryTaskFragment */);
+        mController.addTaskContainer(taskContainer.getTaskId(), taskContainer);
+        final TaskFragmentContainer tf0 = createTaskFragmentContainer(
+                taskContainer, null /* pendingAppearedActivity */, new Intent());
+        final TaskFragmentContainer tf1 = createTaskFragmentContainer(
+                taskContainer, null /* pendingAppearedActivity */, new Intent());
 
         // When tf2 is created with using tf0 as pairedPrimaryContainer, tf2 should be inserted
         // right above tf0.
-        final TaskFragmentContainer tf2 = new TaskFragmentContainer(
-                null /* pendingAppearedActivity */, new Intent(), taskContainer, mController, tf0);
+        final TaskFragmentContainer tf2 = new TaskFragmentContainer.Builder(mController,
+                taskContainer.getTaskId(), null /* activityInTask */)
+                .setPendingAppearedIntent(new Intent())
+                .setPairedPrimaryContainer(tf0)
+                .build();
         assertEquals(0, taskContainer.indexOf(tf0));
         assertEquals(1, taskContainer.indexOf(tf2));
         assertEquals(2, taskContainer.indexOf(tf1));
@@ -569,18 +568,15 @@
     @Test
     public void testNewContainerWithPairedPendingAppearedActivity() {
         final TaskContainer taskContainer = createTestTaskContainer();
-        final TaskFragmentContainer tf0 = new TaskFragmentContainer(
-                createMockActivity(), null /* pendingAppearedIntent */, taskContainer, mController,
-                null /* pairedPrimaryTaskFragment */);
-        final TaskFragmentContainer tf1 = new TaskFragmentContainer(
-                null /* pendingAppearedActivity */, new Intent(), taskContainer, mController,
-                null /* pairedPrimaryTaskFragment */);
+        final TaskFragmentContainer tf0 = createTaskFragmentContainer(taskContainer,
+                createMockActivity(), null /* pendingAppearedIntent */);
+        final TaskFragmentContainer tf1 = createTaskFragmentContainer(taskContainer,
+                null /* pendingAppearedActivity */, new Intent());
 
         // When tf2 is created with pendingAppearedActivity, tf2 should be inserted below any
         // TaskFragment without any Activity.
-        final TaskFragmentContainer tf2 = new TaskFragmentContainer(
-                createMockActivity(), null /* pendingAppearedIntent */, taskContainer, mController,
-                null /* pairedPrimaryTaskFragment */);
+        final TaskFragmentContainer tf2 = createTaskFragmentContainer(taskContainer,
+                createMockActivity(), null /* pendingAppearedIntent */);
         assertEquals(0, taskContainer.indexOf(tf0));
         assertEquals(1, taskContainer.indexOf(tf2));
         assertEquals(2, taskContainer.indexOf(tf1));
@@ -589,9 +585,8 @@
     @Test
     public void testIsVisible() {
         final TaskContainer taskContainer = createTestTaskContainer();
-        final TaskFragmentContainer container = new TaskFragmentContainer(
-                null /* pendingAppearedActivity */, new Intent(), taskContainer, mController,
-                null /* pairedPrimaryTaskFragment */);
+        final TaskFragmentContainer container = createTaskFragmentContainer(taskContainer,
+                null /* pendingAppearedActivity */, new Intent());
 
         // Not visible when there is not appeared.
         assertFalse(container.isVisible());
@@ -617,4 +612,14 @@
         doReturn(activity).when(mController).getActivity(activityToken);
         return activity;
     }
+
+    private TaskFragmentContainer createTaskFragmentContainer(TaskContainer taskContainer,
+            Activity pendingAppearedActivity, Intent pendingAppearedIntent) {
+        final int taskId = taskContainer.getTaskId();
+        mController.addTaskContainer(taskId, taskContainer);
+        return new TaskFragmentContainer.Builder(mController, taskId, pendingAppearedActivity)
+                .setPendingAppearedActivity(pendingAppearedActivity)
+                .setPendingAppearedIntent(pendingAppearedIntent)
+                .build();
+    }
 }
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 08e695b..15f8c32 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -1,3 +1,5 @@
+# proto-file: build/make/tools/aconfig/aconfig_protos/protos/aconfig.proto
+
 package: "com.android.wm.shell"
 container: "system"
 
@@ -99,3 +101,13 @@
     description: "Enable UI affordances to put other content into a bubble"
     bug: "342245211"
 }
+
+flag {
+    name: "only_reuse_bubbled_task_when_launched_from_bubble"
+    namespace: "multitasking"
+    description: "Allow reusing bubbled tasks for new activities only when launching from bubbles"
+    bug: "328229865"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
index 12d1927..ace2c13 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleExpandedViewPinControllerTest.kt
@@ -26,7 +26,7 @@
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
 import com.android.internal.protolog.common.ProtoLog
 import com.android.wm.shell.R
 import com.android.wm.shell.bubbles.BubblePositioner
@@ -35,6 +35,8 @@
 import com.android.wm.shell.common.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_IN_DURATION
 import com.android.wm.shell.common.bubbles.BaseBubblePinController.Companion.DROP_TARGET_ALPHA_OUT_DURATION
 import com.android.wm.shell.common.bubbles.BubbleBarLocation
+import com.android.wm.shell.common.bubbles.BubbleBarLocation.LEFT
+import com.android.wm.shell.common.bubbles.BubbleBarLocation.RIGHT
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
 import org.junit.Before
@@ -63,6 +65,9 @@
     private lateinit var controller: BubbleExpandedViewPinController
     private lateinit var testListener: TestLocationChangeListener
 
+    private val dropTargetView: View?
+        get() = container.findViewById(R.id.bubble_bar_drop_target)
+
     private val pointOnLeft = PointF(100f, 100f)
     private val pointOnRight = PointF(1900f, 500f)
 
@@ -92,13 +97,14 @@
 
     @After
     fun tearDown() {
-        runOnMainSync { controller.onDragEnd() }
+        getInstrumentation().runOnMainSync { controller.onDragEnd() }
         waitForAnimateOut()
     }
 
+    /** Dragging on same side should not show drop target or trigger location changes */
     @Test
-    fun drag_stayOnSameSide() {
-        runOnMainSync {
+    fun drag_stayOnRightSide() {
+        getInstrumentation().runOnMainSync {
             controller.onDragStart(initialLocationOnLeft = false)
             controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
             controller.onDragEnd()
@@ -106,71 +112,124 @@
         waitForAnimateIn()
         assertThat(dropTargetView).isNull()
         assertThat(testListener.locationChanges).isEmpty()
-        assertThat(testListener.locationReleases).containsExactly(BubbleBarLocation.RIGHT)
+        assertThat(testListener.locationReleases).containsExactly(RIGHT)
     }
 
+    /** Dragging on same side should not show drop target or trigger location changes */
     @Test
-    fun drag_toLeft() {
-        // Drag to left, but don't finish
-        runOnMainSync {
+    fun drag_stayOnLeftSide() {
+        getInstrumentation().runOnMainSync {
+            controller.onDragStart(initialLocationOnLeft = true)
+            controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
+            controller.onDragEnd()
+        }
+        waitForAnimateIn()
+        assertThat(dropTargetView).isNull()
+        assertThat(testListener.locationChanges).isEmpty()
+        assertThat(testListener.locationReleases).containsExactly(LEFT)
+    }
+
+    /** Drag crosses to the other side. Show drop target and trigger a location change. */
+    @Test
+    fun drag_rightToLeft() {
+        getInstrumentation().runOnMainSync {
             controller.onDragStart(initialLocationOnLeft = false)
+            controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
             controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
         }
         waitForAnimateIn()
 
         assertThat(dropTargetView).isNotNull()
         assertThat(dropTargetView!!.alpha).isEqualTo(1f)
-
-        val expectedDropTargetBounds = getExpectedDropTargetBounds(onLeft = true)
-        assertThat(dropTargetView!!.layoutParams.width).isEqualTo(expectedDropTargetBounds.width())
-        assertThat(dropTargetView!!.layoutParams.height)
-            .isEqualTo(expectedDropTargetBounds.height())
-
-        assertThat(testListener.locationChanges).containsExactly(BubbleBarLocation.LEFT)
+        assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnLeft())
+        assertThat(testListener.locationChanges).containsExactly(LEFT)
         assertThat(testListener.locationReleases).isEmpty()
-
-        // Finish the drag
-        runOnMainSync { controller.onDragEnd() }
-        assertThat(testListener.locationReleases).containsExactly(BubbleBarLocation.LEFT)
     }
 
+    /** Drag crosses to the other side. Show drop target and trigger a location change. */
     @Test
-    fun drag_toLeftAndBackToRight() {
-        // Drag to left
-        runOnMainSync {
-            controller.onDragStart(initialLocationOnLeft = false)
+    fun drag_leftToRight() {
+        getInstrumentation().runOnMainSync {
+            controller.onDragStart(initialLocationOnLeft = true)
             controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
+            controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
         }
         waitForAnimateIn()
+
+        assertThat(dropTargetView).isNotNull()
+        assertThat(dropTargetView!!.alpha).isEqualTo(1f)
+        assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnRight())
+        assertThat(testListener.locationChanges).containsExactly(RIGHT)
+        assertThat(testListener.locationReleases).isEmpty()
+    }
+
+    /**
+     * Drop target does not initially show on the side that the drag starts. Check that it shows up
+     * after the dragging the view to other side and back to the initial side.
+     */
+    @Test
+    fun drag_rightToLeftToRight() {
+        getInstrumentation().runOnMainSync {
+            controller.onDragStart(initialLocationOnLeft = false)
+            controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
+        }
+        waitForAnimateIn()
+        assertThat(dropTargetView).isNull()
+
+        getInstrumentation().runOnMainSync { controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y) }
+        waitForAnimateIn()
         assertThat(dropTargetView).isNotNull()
 
-        // Drag to right
-        runOnMainSync { controller.onDragUpdate(pointOnRight.x, pointOnRight.y) }
-        // We have to wait for existing drop target to animate out and new to animate in
+        getInstrumentation().runOnMainSync {
+            controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
+        }
         waitForAnimateOut()
         waitForAnimateIn()
-
         assertThat(dropTargetView).isNotNull()
         assertThat(dropTargetView!!.alpha).isEqualTo(1f)
-
-        val expectedDropTargetBounds = getExpectedDropTargetBounds(onLeft = false)
-        assertThat(dropTargetView!!.layoutParams.width).isEqualTo(expectedDropTargetBounds.width())
-        assertThat(dropTargetView!!.layoutParams.height)
-            .isEqualTo(expectedDropTargetBounds.height())
-
-        assertThat(testListener.locationChanges)
-            .containsExactly(BubbleBarLocation.LEFT, BubbleBarLocation.RIGHT)
+        assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnRight())
+        assertThat(testListener.locationChanges).containsExactly(LEFT, RIGHT).inOrder()
         assertThat(testListener.locationReleases).isEmpty()
-
-        // Release the view
-        runOnMainSync { controller.onDragEnd() }
-        assertThat(testListener.locationReleases).containsExactly(BubbleBarLocation.RIGHT)
     }
 
+    /**
+     * Drop target does not initially show on the side that the drag starts. Check that it shows up
+     * after the dragging the view to other side and back to the initial side.
+     */
     @Test
-    fun drag_toLeftInExclusionRect() {
-        runOnMainSync {
+    fun drag_leftToRightToLeft() {
+        getInstrumentation().runOnMainSync {
+            controller.onDragStart(initialLocationOnLeft = true)
+            controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
+        }
+        waitForAnimateIn()
+        assertThat(dropTargetView).isNull()
+
+        getInstrumentation().runOnMainSync {
+            controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
+        }
+        waitForAnimateIn()
+        assertThat(dropTargetView).isNotNull()
+
+        getInstrumentation().runOnMainSync { controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y) }
+        waitForAnimateOut()
+        waitForAnimateIn()
+        assertThat(dropTargetView).isNotNull()
+        assertThat(dropTargetView!!.alpha).isEqualTo(1f)
+        assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnLeft())
+        assertThat(testListener.locationChanges).containsExactly(RIGHT, LEFT).inOrder()
+        assertThat(testListener.locationReleases).isEmpty()
+    }
+
+    /**
+     * Drag from right to left, but stay in exclusion rect around the dismiss view. Drop target
+     * should not show and location change should not trigger.
+     */
+    @Test
+    fun drag_rightToLeft_inExclusionRect() {
+        getInstrumentation().runOnMainSync {
             controller.onDragStart(initialLocationOnLeft = false)
+            controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
             // Exclusion rect is around the bottom center area of the screen
             controller.onDragUpdate(SCREEN_WIDTH / 2f - 50, SCREEN_HEIGHT - 100f)
         }
@@ -178,85 +237,212 @@
         assertThat(dropTargetView).isNull()
         assertThat(testListener.locationChanges).isEmpty()
         assertThat(testListener.locationReleases).isEmpty()
-
-        runOnMainSync { controller.onDragEnd() }
-        assertThat(testListener.locationReleases).containsExactly(BubbleBarLocation.RIGHT)
     }
 
+    /**
+     * Drag from left to right, but stay in exclusion rect around the dismiss view. Drop target
+     * should not show and location change should not trigger.
+     */
     @Test
-    fun toggleSetDropTargetHidden_dropTargetExists() {
-        runOnMainSync {
+    fun drag_leftToRight_inExclusionRect() {
+        getInstrumentation().runOnMainSync {
+            controller.onDragStart(initialLocationOnLeft = true)
+            controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
+            // Exclusion rect is around the bottom center area of the screen
+            controller.onDragUpdate(SCREEN_WIDTH / 2f + 50, SCREEN_HEIGHT - 100f)
+        }
+        waitForAnimateIn()
+        assertThat(dropTargetView).isNull()
+        assertThat(testListener.locationChanges).isEmpty()
+        assertThat(testListener.locationReleases).isEmpty()
+    }
+
+    /**
+     * Drag to dismiss target and back to the same side should not cause the drop target to show.
+     */
+    @Test
+    fun drag_rightToDismissToRight() {
+        getInstrumentation().runOnMainSync {
             controller.onDragStart(initialLocationOnLeft = false)
+            controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
+            controller.onStuckToDismissTarget()
+            controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
+        }
+        waitForAnimateIn()
+        assertThat(dropTargetView).isNull()
+        assertThat(testListener.locationChanges).isEmpty()
+        assertThat(testListener.locationReleases).isEmpty()
+    }
+
+    /**
+     * Drag to dismiss target and back to the same side should not cause the drop target to show.
+     */
+    @Test
+    fun drag_leftToDismissToLeft() {
+        getInstrumentation().runOnMainSync {
+            controller.onDragStart(initialLocationOnLeft = true)
+            controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
+            controller.onStuckToDismissTarget()
             controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
         }
         waitForAnimateIn()
+        assertThat(dropTargetView).isNull()
+        assertThat(testListener.locationChanges).isEmpty()
+        assertThat(testListener.locationReleases).isEmpty()
+    }
 
+    /** Drag to dismiss target and other side should show drop target on the other side. */
+    @Test
+    fun drag_rightToDismissToLeft() {
+        getInstrumentation().runOnMainSync {
+            controller.onDragStart(initialLocationOnLeft = false)
+            controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
+            controller.onStuckToDismissTarget()
+            controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
+        }
+        waitForAnimateIn()
+        assertThat(dropTargetView).isNotNull()
+        assertThat(dropTargetView!!.alpha).isEqualTo(1f)
+        assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnLeft())
+
+        assertThat(testListener.locationChanges).containsExactly(LEFT)
+        assertThat(testListener.locationReleases).isEmpty()
+    }
+
+    /** Drag to dismiss target and other side should show drop target on the other side. */
+    @Test
+    fun drag_leftToDismissToRight() {
+        getInstrumentation().runOnMainSync {
+            controller.onDragStart(initialLocationOnLeft = true)
+            controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
+            controller.onStuckToDismissTarget()
+            controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
+        }
+        waitForAnimateIn()
+        assertThat(dropTargetView).isNotNull()
+        assertThat(dropTargetView!!.alpha).isEqualTo(1f)
+        assertThat(dropTargetView!!.bounds()).isEqualTo(getExpectedDropTargetBoundsOnRight())
+
+        assertThat(testListener.locationChanges).containsExactly(RIGHT)
+        assertThat(testListener.locationReleases).isEmpty()
+    }
+
+    /**
+     * Drag to dismiss should trigger a location change to the initial location, if the current
+     * location is different. And hide the drop target.
+     */
+    @Test
+    fun drag_rightToLeftToDismiss() {
+        getInstrumentation().runOnMainSync {
+            controller.onDragStart(initialLocationOnLeft = false)
+            controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
+            controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
+        }
+        waitForAnimateIn()
         assertThat(dropTargetView).isNotNull()
         assertThat(dropTargetView!!.alpha).isEqualTo(1f)
 
-        runOnMainSync { controller.setDropTargetHidden(true) }
+        getInstrumentation().runOnMainSync { controller.onStuckToDismissTarget() }
         waitForAnimateOut()
-        assertThat(dropTargetView).isNotNull()
         assertThat(dropTargetView!!.alpha).isEqualTo(0f)
 
-        runOnMainSync { controller.setDropTargetHidden(false) }
+        assertThat(testListener.locationChanges).containsExactly(LEFT, RIGHT).inOrder()
+        assertThat(testListener.locationReleases).isEmpty()
+    }
+
+    /**
+     * Drag to dismiss should trigger a location change to the initial location, if the current
+     * location is different. And hide the drop target.
+     */
+    @Test
+    fun drag_leftToRightToDismiss() {
+        getInstrumentation().runOnMainSync {
+            controller.onDragStart(initialLocationOnLeft = true)
+            controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
+            controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
+        }
         waitForAnimateIn()
         assertThat(dropTargetView).isNotNull()
         assertThat(dropTargetView!!.alpha).isEqualTo(1f)
-    }
-
-    @Test
-    fun toggleSetDropTargetHidden_noDropTarget() {
-        runOnMainSync { controller.setDropTargetHidden(true) }
+        getInstrumentation().runOnMainSync { controller.onStuckToDismissTarget() }
         waitForAnimateOut()
-        assertThat(dropTargetView).isNull()
-
-        runOnMainSync { controller.setDropTargetHidden(false) }
-        waitForAnimateIn()
-        assertThat(dropTargetView).isNull()
+        assertThat(dropTargetView!!.alpha).isEqualTo(0f)
+        assertThat(testListener.locationChanges).containsExactly(RIGHT, LEFT).inOrder()
+        assertThat(testListener.locationReleases).isEmpty()
     }
 
+    /** Finishing drag should remove drop target and send location update. */
     @Test
-    fun onDragEnd_dropTargetExists() {
-        runOnMainSync {
+    fun drag_rightToLeftRelease() {
+        getInstrumentation().runOnMainSync {
             controller.onDragStart(initialLocationOnLeft = false)
+            controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
             controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
         }
         waitForAnimateIn()
         assertThat(dropTargetView).isNotNull()
 
-        runOnMainSync { controller.onDragEnd() }
+        getInstrumentation().runOnMainSync { controller.onDragEnd() }
         waitForAnimateOut()
         assertThat(dropTargetView).isNull()
+        assertThat(testListener.locationChanges).containsExactly(LEFT)
+        assertThat(testListener.locationReleases).containsExactly(LEFT)
     }
 
+    /** Finishing drag should remove drop target and send location update. */
     @Test
-    fun onDragEnd_noDropTarget() {
-        runOnMainSync { controller.onDragEnd() }
+    fun drag_leftToRightRelease() {
+        getInstrumentation().runOnMainSync {
+            controller.onDragStart(initialLocationOnLeft = true)
+            controller.onDragUpdate(pointOnLeft.x, pointOnLeft.y)
+            controller.onDragUpdate(pointOnRight.x, pointOnRight.y)
+        }
+        waitForAnimateIn()
+        assertThat(dropTargetView).isNotNull()
+
+        getInstrumentation().runOnMainSync { controller.onDragEnd() }
         waitForAnimateOut()
         assertThat(dropTargetView).isNull()
+        assertThat(testListener.locationChanges).containsExactly(RIGHT)
+        assertThat(testListener.locationReleases).containsExactly(RIGHT)
     }
 
-    private val dropTargetView: View?
-        get() = container.findViewById(R.id.bubble_bar_drop_target)
-
-    private fun getExpectedDropTargetBounds(onLeft: Boolean): Rect =
+    private fun getExpectedDropTargetBoundsOnLeft(): Rect =
         Rect().also {
-            positioner.getBubbleBarExpandedViewBounds(onLeft, false /* isOveflowExpanded */, it)
+            positioner.getBubbleBarExpandedViewBounds(
+                true /* onLeft */,
+                false /* isOverflowExpanded */,
+                it
+            )
         }
 
-    private fun runOnMainSync(runnable: Runnable) {
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(runnable)
-    }
+    private fun getExpectedDropTargetBoundsOnRight(): Rect =
+        Rect().also {
+            positioner.getBubbleBarExpandedViewBounds(
+                false /* onLeft */,
+                false /* isOverflowExpanded */,
+                it
+            )
+        }
 
     private fun waitForAnimateIn() {
         // Advance animator for on-device test
-        runOnMainSync { animatorTestRule.advanceTimeBy(DROP_TARGET_ALPHA_IN_DURATION) }
+        getInstrumentation().runOnMainSync {
+            animatorTestRule.advanceTimeBy(DROP_TARGET_ALPHA_IN_DURATION)
+        }
     }
 
     private fun waitForAnimateOut() {
         // Advance animator for on-device test
-        runOnMainSync { animatorTestRule.advanceTimeBy(DROP_TARGET_ALPHA_OUT_DURATION) }
+        getInstrumentation().runOnMainSync {
+            animatorTestRule.advanceTimeBy(DROP_TARGET_ALPHA_OUT_DURATION)
+        }
+    }
+
+    private fun View.bounds(): Rect {
+        return Rect(0, 0, layoutParams.width, layoutParams.height).also { rect ->
+            rect.offsetTo(x.toInt(), y.toInt())
+        }
     }
 
     internal class TestLocationChangeListener : BaseBubblePinController.LocationChangeListener {
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index 81d066f..532ecc6 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -56,7 +56,7 @@
     <string name="one_handed_tutorial_description" msgid="3486582858591353067">"Каб выйсці, правядзіце па экране пальцам знізу ўверх або націсніце ў любым месцы над праграмай"</string>
     <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Запусціць рэжым кіравання адной рукой"</string>
     <string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Выйсці з рэжыму кіравання адной рукой"</string>
-    <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Налады ўсплывальных апавяшчэнняў у праграме \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
+    <string name="bubbles_settings_button_description" msgid="1301286017420516912">"Налады ўсплывальных чатаў у праграме \"<xliff:g id="APP_NAME">%1$s</xliff:g>\""</string>
     <string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Дадатковае меню"</string>
     <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Зноў дадаць у стос"</string>
     <string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> ад праграмы \"<xliff:g id="APP_NAME">%2$s</xliff:g>\""</string>
@@ -70,16 +70,16 @@
     <string name="bubbles_app_settings" msgid="3617224938701566416">"Налады \"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>\""</string>
     <string name="bubble_dismiss_text" msgid="8816558050659478158">"Адхіліць апавяшчэнне"</string>
     <string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Не паказваць размову ў выглядзе ўсплывальных апавяшчэнняў"</string>
-    <string name="bubbles_user_education_title" msgid="2112319053732691899">"Усплывальныя апавяшчэнні"</string>
-    <string name="bubbles_user_education_description" msgid="4215862563054175407">"Новыя размовы будуць паказвацца як рухомыя значкі ці ўсплывальныя апавяшчэнні. Націсніце, каб адкрыць усплывальнае апавяшчэнне. Перацягніце яго, каб перамясціць."</string>
-    <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Кіруйце ўсплывальнымі апавяшчэннямі ў любы час"</string>
-    <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Каб выключыць усплывальныя апавяшчэнні з гэтай праграмы, націсніце \"Кіраваць\""</string>
+    <string name="bubbles_user_education_title" msgid="2112319053732691899">"Усплывальныя чаты"</string>
+    <string name="bubbles_user_education_description" msgid="4215862563054175407">"Новыя размовы будуць паказвацца як рухомыя значкі ці ўсплывальныя чаты. Націсніце, каб адкрыць усплывальны чат. Перацягніце яго, каб перамясціць."</string>
+    <string name="bubbles_user_education_manage_title" msgid="7042699946735628035">"Кіруйце ўсплывальнымі чатамі"</string>
+    <string name="bubbles_user_education_manage" msgid="3460756219946517198">"Каб выключыць усплывальныя чаты з гэтай праграмы, націсніце \"Кіраваць\""</string>
     <string name="bubbles_user_education_got_it" msgid="3382046149225428296">"Зразумела"</string>
-    <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Няма нядаўніх усплывальных апавяшчэнняў"</string>
-    <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Нядаўнія і адхіленыя ўсплывальныя апавяшчэнні будуць паказаны тут"</string>
-    <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Чат з выкарыстаннем усплывальных апавяшчэнняў"</string>
+    <string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Няма нядаўніх усплывальных чатаў"</string>
+    <string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Нядаўнія і адхіленыя ўсплывальныя чаты будуць паказаны тут"</string>
+    <string name="bubble_bar_education_stack_title" msgid="2486903590422497245">"Усплывальныя чаты"</string>
     <string name="bubble_bar_education_stack_text" msgid="2446934610817409820">"Новыя размовы паказваюцца ў выглядзе значкоў у ніжнім вугле экрана. Націсніце на іх, каб разгарнуць. Перацягніце іх, калі хочаце закрыць."</string>
-    <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Кіруйце наладамі ўсплывальных апавяшчэнняў у любы час"</string>
+    <string name="bubble_bar_education_manage_title" msgid="6148404487810835924">"Кіруйце наладамі ўсплывальных чатаў у любы час"</string>
     <string name="bubble_bar_education_manage_text" msgid="3199732148641842038">"Каб кіраваць усплывальнымі апавяшчэннямі для праграм і размоў, націсніце тут"</string>
     <string name="notification_bubble_title" msgid="6082910224488253378">"Усплывальнае апавяшчэнне"</string>
     <string name="manage_bubbles_text" msgid="7730624269650594419">"Кіраваць"</string>
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
index 6ca6517..dc022b4 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/TransitionUtil.java
@@ -69,7 +69,7 @@
 
     /** Returns {@code true} if the transition is opening or closing mode. */
     public static boolean isOpenOrCloseMode(@TransitionInfo.TransitionMode int mode) {
-        return isOpeningMode(mode) || mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK;
+        return isOpeningMode(mode) || isClosingMode(mode);
     }
 
     /** Returns {@code true} if the transition is opening mode. */
@@ -77,6 +77,11 @@
         return mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT;
     }
 
+    /** Returns {@code true} if the transition is closing mode. */
+    public static boolean isClosingMode(@TransitionInfo.TransitionMode int mode) {
+        return mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK;
+    }
+
     /** Returns {@code true} if the transition has a display change. */
     public static boolean hasDisplayChange(@NonNull TransitionInfo info) {
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
index f2095b1..3ded7d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -175,6 +175,7 @@
             .setName("home_task_overlay_container")
             .setContainerLayer()
             .setHidden(false)
+            .setCallsite("ShellTaskOrganizer.mHomeTaskOverlayContainer")
             .build();
 
     /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
index ee740fb..9114c7a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
@@ -27,6 +27,7 @@
 import android.graphics.Rect
 import android.graphics.RectF
 import android.os.RemoteException
+import android.util.TimeUtils
 import android.view.Choreographer
 import android.view.Display
 import android.view.IRemoteAnimationFinishedCallback
@@ -109,6 +110,7 @@
     private val postCommitFlingSpring = SpringForce(SPRING_SCALE)
             .setStiffness(SpringForce.STIFFNESS_LOW)
             .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
+    protected var gestureProgress = 0f
 
     /** Background color to be used during the animation, also see [getBackgroundColor] */
     protected var customizedBackgroundColor = 0
@@ -212,6 +214,7 @@
 
     private fun onGestureProgress(backEvent: BackEvent) {
         val progress = gestureInterpolator.getInterpolation(backEvent.progress)
+        gestureProgress = progress
         currentClosingRect.setInterpolatedRectF(startClosingRect, targetClosingRect, progress)
         val yOffset = getYOffset(currentClosingRect, backEvent.touchY)
         currentClosingRect.offset(0f, yOffset)
@@ -257,12 +260,16 @@
         }
 
         // kick off spring animation with the current velocity from the pre-commit phase, this
-        // affects the scaling of the closing activity during post-commit
+        // affects the scaling of the closing and/or opening activity during post-commit
+        val startVelocity =
+            if (gestureProgress < 0.1f) -DEFAULT_FLING_VELOCITY else -velocity * SPRING_SCALE
         val flingAnimation = SpringAnimation(postCommitFlingScale, SPRING_SCALE)
-            .setStartVelocity(min(0f, -velocity * SPRING_SCALE))
+            .setStartVelocity(startVelocity.coerceIn(-MAX_FLING_VELOCITY, 0f))
             .setStartValue(SPRING_SCALE)
             .setSpring(postCommitFlingSpring)
         flingAnimation.start()
+        // do an animation-frame immediately to prevent idle frame
+        flingAnimation.doAnimationFrame(choreographer.lastFrameTimeNanos / TimeUtils.NANOS_PER_MS)
 
         val valueAnimator =
             ValueAnimator.ofFloat(1f, 0f).setDuration(getPostCommitAnimationDuration())
@@ -292,6 +299,7 @@
         enteringTarget?.let {
             if (it.leash != null && it.leash.isValid) {
                 transaction.setCornerRadius(it.leash, 0f)
+                if (!triggerBack) transaction.setAlpha(it.leash, 0f)
                 it.leash.release()
             }
             enteringTarget = null
@@ -315,19 +323,22 @@
         isLetterboxed = false
         enteringHasSameLetterbox = false
         lastPostCommitFlingScale = SPRING_SCALE
+        gestureProgress = 0f
     }
 
     protected fun applyTransform(
         leash: SurfaceControl?,
         rect: RectF,
         alpha: Float,
-        baseTransformation: Transformation? = null
+        baseTransformation: Transformation? = null,
+        flingMode: FlingMode = FlingMode.NO_FLING
     ) {
         if (leash == null || !leash.isValid) return
         tempRectF.set(rect)
-        if (leash == closingTarget?.leash) {
-            lastPostCommitFlingScale = (postCommitFlingScale.value / SPRING_SCALE).coerceIn(
-                    minimumValue = MAX_FLING_SCALE, maximumValue = lastPostCommitFlingScale
+        if (flingMode != FlingMode.NO_FLING) {
+            lastPostCommitFlingScale = min(
+                postCommitFlingScale.value / SPRING_SCALE,
+                if (flingMode == FlingMode.FLING_BOUNCE) 1f else lastPostCommitFlingScale
             )
             // apply an additional scale to the closing target to account for fling velocity
             tempRectF.scaleCentered(lastPostCommitFlingScale)
@@ -529,14 +540,30 @@
         private const val MAX_SCRIM_ALPHA_DARK = 0.8f
         private const val MAX_SCRIM_ALPHA_LIGHT = 0.2f
         private const val SPRING_SCALE = 100f
-        private const val MAX_FLING_SCALE = 0.6f
+        private const val MAX_FLING_VELOCITY = 1000f
+        private const val DEFAULT_FLING_VELOCITY = 120f
+    }
+
+    enum class FlingMode {
+        NO_FLING,
+
+        /**
+         * This is used for the closing target in custom cross-activity back animations. When the
+         * back gesture is flung, the closing target shrinks a bit further with a spring motion.
+         */
+        FLING_SHRINK,
+
+        /**
+         * This is used for the closing and opening target in the default cross-activity back
+         * animation. When the back gesture is flung, the closing and opening targets shrink a
+         * bit further and then bounce back with a spring motion.
+         */
+        FLING_BOUNCE
     }
 }
 
 // The target will loose focus when alpha == 0, so keep a minimum value for it.
-private fun keepMinimumAlpha(transAlpha: Float): Float {
-    return max(transAlpha.toDouble(), 0.005).toFloat()
-}
+private fun keepMinimumAlpha(transAlpha: Float) = max(transAlpha, 0.005f)
 
 private fun isDarkMode(context: Context): Boolean {
     return context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
index c4aafea..c738ce5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
@@ -57,7 +57,6 @@
     private var enterAnimation: Animation? = null
     private var closeAnimation: Animation? = null
     private val transformation = Transformation()
-    private var gestureProgress = 0f
 
     override val allowEnteringYShift = false
 
@@ -105,7 +104,6 @@
     }
 
     override fun getPreCommitEnteringBaseTransformation(progress: Float): Transformation {
-        gestureProgress = progress
         transformation.clear()
         enterAnimation!!.getTransformationAt(progress * PRE_COMMIT_MAX_PROGRESS, transformation)
         return transformation
@@ -134,7 +132,13 @@
         if (closingTarget == null || enteringTarget == null) return
 
         val closingProgress = closeAnimation!!.getPostCommitProgress(linearProgress)
-        applyTransform(closingTarget!!.leash, currentClosingRect, closingProgress, closeAnimation!!)
+        applyTransform(
+            closingTarget!!.leash,
+            currentClosingRect,
+            closingProgress,
+            closeAnimation!!,
+            FlingMode.FLING_SHRINK
+        )
         val enteringProgress = MathUtils.lerp(
             gestureProgress * PRE_COMMIT_MAX_PROGRESS,
             1f,
@@ -144,7 +148,8 @@
             enteringTarget!!.leash,
             currentEnteringRect,
             enteringProgress,
-            enterAnimation!!
+            enterAnimation!!,
+            FlingMode.NO_FLING
         )
         applyTransaction()
     }
@@ -153,11 +158,12 @@
         leash: SurfaceControl,
         rect: RectF,
         progress: Float,
-        animation: Animation
+        animation: Animation,
+        flingMode: FlingMode
     ) {
         transformation.clear()
         animation.getTransformationAt(progress, transformation)
-        applyTransform(leash, rect, transformation.alpha, transformation)
+        applyTransform(leash, rect, transformation.alpha, transformation, flingMode)
     }
 
     override fun finishAnimation() {
@@ -166,7 +172,6 @@
         enterAnimation?.reset()
         enterAnimation = null
         transformation.clear()
-        gestureProgress = 0f
         super.finishAnimation()
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
index 44752fe..3b5eb36 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
@@ -43,7 +43,7 @@
         Choreographer.getInstance()
     ) {
 
-    private val postCommitInterpolator = Interpolators.FAST_OUT_SLOW_IN
+    private val postCommitInterpolator = Interpolators.EMPHASIZED
     private val enteringStartOffset =
         context.resources.getDimension(R.dimen.cross_activity_back_entering_start_offset)
     override val allowEnteringYShift = true
@@ -87,17 +87,27 @@
 
     override fun onPostCommitProgress(linearProgress: Float) {
         super.onPostCommitProgress(linearProgress)
-        val closingAlpha = max(1f - linearProgress * 2, 0f)
+        val closingAlpha = max(1f - linearProgress * 5, 0f)
         val progress = postCommitInterpolator.getInterpolation(linearProgress)
         currentClosingRect.setInterpolatedRectF(startClosingRect, targetClosingRect, progress)
-        applyTransform(closingTarget?.leash, currentClosingRect, closingAlpha)
+        applyTransform(
+            closingTarget?.leash,
+            currentClosingRect,
+            closingAlpha,
+            flingMode = FlingMode.FLING_BOUNCE
+        )
         currentEnteringRect.setInterpolatedRectF(startEnteringRect, targetEnteringRect, progress)
-        applyTransform(enteringTarget?.leash, currentEnteringRect, 1f)
+        applyTransform(
+            enteringTarget?.leash,
+            currentEnteringRect,
+            1f,
+            flingMode = FlingMode.FLING_BOUNCE
+        )
         applyTransaction()
     }
 
 
     companion object {
-        private const val POST_COMMIT_DURATION = 300L
+        private const val POST_COMMIT_DURATION = 450L
     }
 }
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 bc63db3..fac9bf6 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
@@ -344,7 +344,7 @@
             pw.println("Expanded bubble state:");
             pw.println("  expandedBubbleKey: " + mExpandedBubble.getKey());
 
-            final BubbleExpandedView expandedView = mExpandedBubble.getExpandedView();
+            final BubbleExpandedView expandedView = getExpandedView();
 
             if (expandedView != null) {
                 pw.println("  expandedViewVis:    " + expandedView.getVisibility());
@@ -815,10 +815,11 @@
 
         private float getScrimAlphaForDrag(float dragAmount) {
             // dragAmount should be negative as we allow scroll up only
-            if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+            BubbleExpandedView expandedView = getExpandedView();
+            if (expandedView != null) {
                 float alphaRange = BUBBLE_EXPANDED_SCRIM_ALPHA - MIN_SCRIM_ALPHA_FOR_DRAG;
 
-                int dragMax = mExpandedBubble.getExpandedView().getContentHeight();
+                int dragMax = expandedView.getContentHeight();
                 float dragFraction = dragAmount / dragMax;
 
                 return Math.max(BUBBLE_EXPANDED_SCRIM_ALPHA - alphaRange * dragFraction,
@@ -1120,33 +1121,35 @@
         mExpandedViewAlphaAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
-                if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+                BubbleExpandedView expandedView = getExpandedView();
+                if (expandedView != null) {
                     // We need to be Z ordered on top in order for alpha animations to work.
-                    mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(true);
-                    mExpandedBubble.getExpandedView().setAnimating(true);
+                    expandedView.setSurfaceZOrderedOnTop(true);
+                    expandedView.setAnimating(true);
                     mExpandedViewContainer.setVisibility(VISIBLE);
                 }
             }
 
             @Override
             public void onAnimationEnd(Animator animation) {
-                if (mExpandedBubble != null
-                        && mExpandedBubble.getExpandedView() != null
+                BubbleExpandedView expandedView = getExpandedView();
+                if (expandedView != null
                         // The surface needs to be Z ordered on top for alpha values to work on the
                         // TaskView, and if we're temporarily hidden, we are still on the screen
                         // with alpha = 0f until we animate back. Stay Z ordered on top so the alpha
                         // = 0f remains in effect.
                         && !mExpandedViewTemporarilyHidden) {
-                    mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false);
-                    mExpandedBubble.getExpandedView().setAnimating(false);
+                    expandedView.setSurfaceZOrderedOnTop(false);
+                    expandedView.setAnimating(false);
                 }
             }
         });
         mExpandedViewAlphaAnimator.addUpdateListener(valueAnimator -> {
-            if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+            BubbleExpandedView expandedView = getExpandedView();
+            if (expandedView != null) {
                 float alpha = (float) valueAnimator.getAnimatedValue();
-                mExpandedBubble.getExpandedView().setContentAlpha(alpha);
-                mExpandedBubble.getExpandedView().setBackgroundAlpha(alpha);
+                expandedView.setContentAlpha(alpha);
+                expandedView.setBackgroundAlpha(alpha);
             }
         });
 
@@ -1390,7 +1393,7 @@
         }
         final boolean seen = getPrefBoolean(ManageEducationView.PREF_MANAGED_EDUCATION);
         final boolean shouldShow = (!seen || BubbleDebugConfig.forceShowUserEducation(mContext))
-                && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null;
+                && getExpandedView() != null;
         ProtoLog.d(WM_SHELL_BUBBLES, "Show manage edu=%b", shouldShow);
         if (shouldShow && BubbleDebugConfig.neverShowUserEducation(mContext)) {
             Log.w(TAG, "Want to show manage edu, but it is forced hidden");
@@ -1417,9 +1420,9 @@
      * Show manage education if was not showing before.
      */
     private void showManageEdu() {
-        if (mExpandedBubble == null || mExpandedBubble.getExpandedView() == null) return;
-        mManageEduView.show(mExpandedBubble.getExpandedView(),
-                mStackAnimationController.isStackOnLeftSide());
+        BubbleExpandedView expandedView = getExpandedView();
+        if (expandedView == null) return;
+        mManageEduView.show(expandedView, mStackAnimationController.isStackOnLeftSide());
     }
 
     @VisibleForTesting
@@ -1931,6 +1934,11 @@
         return mExpandedBubble;
     }
 
+    @Nullable
+    private BubbleExpandedView getExpandedView() {
+        return mExpandedBubble != null ? mExpandedBubble.getExpandedView() : null;
+    }
+
     // via BubbleData.Listener
     @SuppressLint("ClickableViewAccessibility")
     void addBubble(Bubble bubble) {
@@ -2110,13 +2118,11 @@
 
         // If we're expanded, screenshot the currently expanded bubble (before expanding the newly
         // selected bubble) so we can animate it out.
-        if (mIsExpanded && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null
-                && !mExpandedViewTemporarilyHidden) {
-            if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
-                // Before screenshotting, have the real TaskView show on top of other surfaces
-                // so that the screenshot doesn't flicker on top of it.
-                mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(true);
-            }
+        BubbleExpandedView expandedView = getExpandedView();
+        if (mIsExpanded && expandedView != null && !mExpandedViewTemporarilyHidden) {
+            // Before screenshotting, have the real TaskView show on top of other surfaces
+            // so that the screenshot doesn't flicker on top of it.
+            expandedView.setSurfaceZOrderedOnTop(true);
 
             try {
                 screenshotAnimatingOutBubbleIntoSurface((success) -> {
@@ -2136,7 +2142,7 @@
     private void showNewlySelectedBubble(BubbleViewProvider bubbleToSelect) {
         final BubbleViewProvider previouslySelected = mExpandedBubble;
         mExpandedBubble = bubbleToSelect;
-        mExpandedViewAnimationController.setExpandedView(mExpandedBubble.getExpandedView());
+        mExpandedViewAnimationController.setExpandedView(getExpandedView());
 
         if (mIsExpanded) {
             hideCurrentInputMethod();
@@ -2445,8 +2451,7 @@
             mBubbleContainer.animate().translationX(0).start();
         }
         mExpandedAnimationController.expandFromStack(() -> {
-            if (mIsExpanded && mExpandedBubble != null
-                    && mExpandedBubble.getExpandedView() != null) {
+            if (mIsExpanded && getExpandedView() != null) {
                 maybeShowManageEdu();
             }
             updateOverflowDotVisibility(true /* expanding */);
@@ -2509,13 +2514,14 @@
         }
         mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix);
 
-        if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
-            mExpandedBubble.getExpandedView().setContentAlpha(0f);
-            mExpandedBubble.getExpandedView().setBackgroundAlpha(0f);
+        BubbleExpandedView expandedView = getExpandedView();
+        if (expandedView != null) {
+            expandedView.setContentAlpha(0f);
+            expandedView.setBackgroundAlpha(0f);
 
             // We'll be starting the alpha animation after a slight delay, so set this flag early
             // here.
-            mExpandedBubble.getExpandedView().setAnimating(true);
+            expandedView.setAnimating(true);
         }
 
         mDelayedAnimation = () -> {
@@ -2545,10 +2551,9 @@
                     .withEndActions(() -> {
                         mExpandedViewContainer.setAnimationMatrix(null);
                         afterExpandedViewAnimation();
-                        if (mExpandedBubble != null
-                                && mExpandedBubble.getExpandedView() != null) {
-                            mExpandedBubble.getExpandedView()
-                                    .setSurfaceZOrderedOnTop(false);
+                        BubbleExpandedView expView = getExpandedView();
+                        if (expView != null) {
+                            expView.setSurfaceZOrderedOnTop(false);
                         }
                     })
                     .start();
@@ -2613,12 +2618,13 @@
         };
         mExpandedViewAnimationController.animateCollapse(collapseBackToStack, after,
                 collapsePosition);
-        if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+        BubbleExpandedView expandedView = getExpandedView();
+        if (expandedView != null) {
             // When the animation completes, we should no longer be showing the content.
             // This won't actually update content visibility immediately, if we are currently
             // animating. But updates the internal state for the content to be hidden after
             // animation completes.
-            mExpandedBubble.getExpandedView().setContentVisibility(false);
+            expandedView.setContentVisibility(false);
         }
     }
 
@@ -2710,10 +2716,10 @@
                         // expanded view animation might not actually set the z ordering for the
                         // expanded view correctly, because the view may still be temporarily
                         // hidden. So set it again here.
-                        BubbleExpandedView bev = mExpandedBubble.getExpandedView();
-                        if (bev != null) {
-                            mExpandedBubble.getExpandedView().setSurfaceZOrderedOnTop(false);
-                            mExpandedBubble.getExpandedView().setAnimating(false);
+                        BubbleExpandedView expandedView = getExpandedView();
+                        if (expandedView != null) {
+                            expandedView.setSurfaceZOrderedOnTop(false);
+                            expandedView.setAnimating(false);
                         }
                     })
                     .start();
@@ -2785,13 +2791,13 @@
 
         if (mIsExpanded) {
             mExpandedViewAnimationController.animateForImeVisibilityChange(visible);
-            if (mPositioner.showBubblesVertically()
-                    && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+            BubbleExpandedView expandedView = getExpandedView();
+            if (mPositioner.showBubblesVertically() && expandedView != null) {
                 float selectedY = mPositioner.getExpandedBubbleXY(getState().selectedIndex,
                         getState()).y;
                 float newExpandedViewTop = mPositioner.getExpandedViewY(mExpandedBubble, selectedY);
-                mExpandedBubble.getExpandedView().setImeVisible(visible);
-                if (!mExpandedBubble.getExpandedView().isUsingMaxHeight()) {
+                expandedView.setImeVisible(visible);
+                if (!expandedView.isUsingMaxHeight()) {
                     mExpandedViewContainer.animate().translationY(newExpandedViewTop);
                 }
                 List<Animator> animList = new ArrayList<>();
@@ -3148,7 +3154,8 @@
 
         // This should not happen, since the manage menu is only visible when there's an expanded
         // bubble. If we end up in this state, just hide the menu immediately.
-        if (mExpandedBubble == null || mExpandedBubble.getExpandedView() == null) {
+        BubbleExpandedView expandedView = getExpandedView();
+        if (expandedView == null) {
             mManageMenu.setVisibility(View.INVISIBLE);
             mManageMenuScrim.setVisibility(INVISIBLE);
             mSysuiProxyProvider.getSysuiProxy().onManageMenuExpandChanged(false /* show */);
@@ -3194,8 +3201,8 @@
             }
         }
 
-        if (mExpandedBubble.getExpandedView().getTaskView() != null) {
-            mExpandedBubble.getExpandedView().getTaskView().setObscuredTouchRect(mShowingManage
+        if (expandedView.getTaskView() != null) {
+            expandedView.getTaskView().setObscuredTouchRect(mShowingManage
                     ? new Rect(0, 0, getWidth(), getHeight())
                     : null);
         }
@@ -3205,8 +3212,8 @@
 
         // When the menu is open, it should be at these coordinates. The menu pops out to the right
         // in LTR and to the left in RTL.
-        mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect);
-        final float margin = mExpandedBubble.getExpandedView().getManageButtonMargin();
+        expandedView.getManageButtonBoundsOnScreen(mTempRect);
+        final float margin = expandedView.getManageButtonMargin();
         final float targetX = isLtr
                 ? mTempRect.left - margin
                 : mTempRect.right + margin - mManageMenu.getWidth();
@@ -3230,9 +3237,10 @@
                     .withEndActions(() -> {
                         View child = mManageMenu.getChildAt(0);
                         child.requestAccessibilityFocus();
-                        if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+                        BubbleExpandedView expView = getExpandedView();
+                        if (expView != null) {
                             // Update the AV's obscured touchable region for the new state.
-                            mExpandedBubble.getExpandedView().updateObscuredTouchableRegion();
+                            expView.updateObscuredTouchableRegion();
                         }
                     })
                     .start();
@@ -3247,9 +3255,10 @@
                     .spring(DynamicAnimation.TRANSLATION_Y, targetY + menuHeight / 4f)
                     .withEndActions(() -> {
                         mManageMenu.setVisibility(View.INVISIBLE);
-                        if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+                        BubbleExpandedView expView = getExpandedView();
+                        if (expView != null) {
                             // Update the AV's obscured touchable region for the new state.
-                            mExpandedBubble.getExpandedView().updateObscuredTouchableRegion();
+                            expView.updateObscuredTouchableRegion();
                         }
                     })
                     .start();
@@ -3276,9 +3285,8 @@
 
     private void updateExpandedBubble() {
         mExpandedViewContainer.removeAllViews();
-        if (mIsExpanded && mExpandedBubble != null
-                && mExpandedBubble.getExpandedView() != null) {
-            BubbleExpandedView bev = mExpandedBubble.getExpandedView();
+        BubbleExpandedView bev = getExpandedView();
+        if (mIsExpanded && bev != null) {
             bev.setContentVisibility(false);
             bev.setAnimating(!mIsExpansionAnimating);
             mExpandedViewContainerMatrix.setScaleX(0f);
@@ -3306,9 +3314,8 @@
     }
 
     private void updateManageButtonListener() {
-        if (mIsExpanded && mExpandedBubble != null
-                && mExpandedBubble.getExpandedView() != null) {
-            BubbleExpandedView bev = mExpandedBubble.getExpandedView();
+        BubbleExpandedView bev = getExpandedView();
+        if (mIsExpanded && bev != null) {
             bev.setManageClickListener((view) -> {
                 showManageMenu(true /* show */);
             });
@@ -3325,14 +3332,13 @@
      *                   expanded bubble.
      */
     private void screenshotAnimatingOutBubbleIntoSurface(Consumer<Boolean> onComplete) {
-        if (!mIsExpanded || mExpandedBubble == null || mExpandedBubble.getExpandedView() == null) {
+        final BubbleExpandedView animatingOutExpandedView = getExpandedView();
+        if (!mIsExpanded || animatingOutExpandedView == null) {
             // You can't animate null.
             onComplete.accept(false);
             return;
         }
 
-        final BubbleExpandedView animatingOutExpandedView = mExpandedBubble.getExpandedView();
-
         // Release the previous screenshot if it hasn't been released already.
         if (mAnimatingOutBubbleBuffer != null) {
             releaseAnimatingOutBubbleBuffer();
@@ -3364,8 +3370,7 @@
         mAnimatingOutSurfaceContainer.setTranslationX(translationX);
         mAnimatingOutSurfaceContainer.setTranslationY(0);
 
-        final int[] taskViewLocation =
-                mExpandedBubble.getExpandedView().getTaskViewLocationOnScreen();
+        final int[] taskViewLocation = animatingOutExpandedView.getTaskViewLocationOnScreen();
         final int[] surfaceViewLocation = mAnimatingOutSurfaceView.getLocationOnScreen();
 
         // Translate the surface to overlap the real TaskView.
@@ -3427,15 +3432,15 @@
         int[] paddings = mPositioner.getExpandedViewContainerPadding(
                 mStackAnimationController.isStackOnLeftSide(), isOverflowExpanded);
         mExpandedViewContainer.setPadding(paddings[0], paddings[1], paddings[2], paddings[3]);
-        if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
+        BubbleExpandedView expandedView = getExpandedView();
+        if (expandedView != null) {
             PointF p = mPositioner.getExpandedBubbleXY(getBubbleIndex(mExpandedBubble),
                     getState());
             mExpandedViewContainer.setTranslationY(mPositioner.getExpandedViewY(mExpandedBubble,
                     mPositioner.showBubblesVertically() ? p.y : p.x));
             mExpandedViewContainer.setTranslationX(0f);
-            mExpandedBubble.getExpandedView().updateTaskViewContentWidth();
-            mExpandedBubble.getExpandedView().updateView(
-                    mExpandedViewContainer.getLocationOnScreen());
+            expandedView.updateTaskViewContentWidth();
+            expandedView.updateView(mExpandedViewContainer.getLocationOnScreen());
             updatePointerPosition(false /* forIme */);
         }
 
@@ -3508,7 +3513,8 @@
      *               the pointer is animated to the location.
      */
     private void updatePointerPosition(boolean forIme) {
-        if (mExpandedBubble == null || mExpandedBubble.getExpandedView() == null) {
+        BubbleExpandedView expandedView = getExpandedView();
+        if (mExpandedBubble == null || expandedView == null) {
             return;
         }
         int index = getBubbleIndex(mExpandedBubble);
@@ -3519,7 +3525,7 @@
         float bubblePosition = mPositioner.showBubblesVertically()
                 ? position.y
                 : position.x;
-        mExpandedBubble.getExpandedView().setPointerPosition(bubblePosition,
+        expandedView.setPointerPosition(bubblePosition,
                 mStackOnLeftOrWillBe, forIme /* animate */);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
index a51ac63..fa1091c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewDragController.kt
@@ -150,7 +150,7 @@
             draggedObject: MagnetizedObject<*>
         ) {
             isStuckToDismiss = true
-            pinController.setDropTargetHidden(true)
+            pinController.onStuckToDismissTarget()
         }
 
         override fun onUnstuckFromTarget(
@@ -162,7 +162,6 @@
         ) {
             isStuckToDismiss = false
             animationHelper.animateUnstuckFromDismissView(target)
-            pinController.setDropTargetHidden(false)
         }
 
         override fun onReleasedInTarget(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt
index e514f9d..eec2468 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BaseBubblePinController.kt
@@ -38,8 +38,10 @@
  */
 abstract class BaseBubblePinController(private val screenSizeProvider: () -> Point) {
 
+    private var initialLocationOnLeft = false
     private var onLeft = false
     private var dismissZone: RectF? = null
+    private var stuckToDismissTarget = false
     private var screenCenterX = 0
     private var listener: LocationChangeListener? = null
     private var dropTargetAnimator: ObjectAnimator? = null
@@ -50,6 +52,7 @@
      * @param initialLocationOnLeft side of the screen where bubble bar is pinned to
      */
     fun onDragStart(initialLocationOnLeft: Boolean) {
+        this.initialLocationOnLeft = initialLocationOnLeft
         onLeft = initialLocationOnLeft
         screenCenterX = screenSizeProvider.invoke().x / 2
         dismissZone = getExclusionRect()
@@ -59,22 +62,33 @@
     fun onDragUpdate(x: Float, y: Float) {
         if (dismissZone?.contains(x, y) == true) return
 
-        if (onLeft && x > screenCenterX) {
-            onLeft = false
-            onLocationChange(RIGHT)
-        } else if (!onLeft && x < screenCenterX) {
-            onLeft = true
-            onLocationChange(LEFT)
+        val wasOnLeft = onLeft
+        onLeft = x < screenCenterX
+        if (wasOnLeft != onLeft) {
+            onLocationChange(if (onLeft) LEFT else RIGHT)
+        } else if (stuckToDismissTarget) {
+            // Moved out of the dismiss view back to initial side, if we have a drop target, show it
+            getDropTargetView()?.apply { animateIn() }
         }
+        // Make sure this gets cleared
+        stuckToDismissTarget = false
     }
 
-    /** Temporarily hide the drop target view */
-    fun setDropTargetHidden(hidden: Boolean) {
-        val targetView = getDropTargetView() ?: return
-        if (hidden) {
-            targetView.animateOut()
-        } else {
-            targetView.animateIn()
+    /** Signal the controller that view has been dragged to dismiss view. */
+    fun onStuckToDismissTarget() {
+        stuckToDismissTarget = true
+        // Notify that location may be reset
+        val shouldResetLocation = onLeft != initialLocationOnLeft
+        if (shouldResetLocation) {
+            onLeft = initialLocationOnLeft
+            listener?.onChange(if (onLeft) LEFT else RIGHT)
+        }
+        getDropTargetView()?.apply {
+            animateOut {
+                if (shouldResetLocation) {
+                    updateLocation(if (onLeft) LEFT else RIGHT)
+                }
+            }
         }
     }
 
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 fb0a1ab..12bbd51 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
@@ -522,14 +522,16 @@
             RecentsTransitionHandler recentsTransitionHandler,
             MultiInstanceHelper multiInstanceHelper,
             @ShellMainThread ShellExecutor mainExecutor,
-            Optional<DesktopTasksLimiter> desktopTasksLimiter) {
+            Optional<DesktopTasksLimiter> desktopTasksLimiter,
+            Optional<RecentTasksController> recentTasksController) {
         return new DesktopTasksController(context, shellInit, shellCommandHandler, shellController,
                 displayController, shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer,
                 dragAndDropController, transitions, enterDesktopTransitionHandler,
                 exitDesktopTransitionHandler, toggleResizeDesktopTaskTransitionHandler,
                 dragToDesktopTransitionHandler, desktopModeTaskRepository,
                 desktopModeLoggerTransitionObserver, launchAdjacentController,
-                recentsTransitionHandler, multiInstanceHelper, mainExecutor, desktopTasksLimiter);
+                recentsTransitionHandler, multiInstanceHelper,
+                mainExecutor, desktopTasksLimiter, recentTasksController.orElse(null));
     }
 
     @WMSingleton
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 38db1eb..ed0d2b8 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
@@ -223,6 +223,7 @@
         mLeash = builder
                 .setName("Desktop Mode Visual Indicator")
                 .setContainerLayer()
+                .setCallsite("DesktopModeVisualIndicator.createView")
                 .build();
         t.show(mLeash);
         final WindowManager.LayoutParams lp =
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 831c6cf..ee7e4d1 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
@@ -70,6 +70,7 @@
 import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.DragToDesktopStateListener
 import com.android.wm.shell.draganddrop.DragAndDropController
 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
+import com.android.wm.shell.recents.RecentTasksController
 import com.android.wm.shell.recents.RecentsTransitionHandler
 import com.android.wm.shell.recents.RecentsTransitionStateListener
 import com.android.wm.shell.shared.DesktopModeStatus
@@ -118,6 +119,7 @@
     private val multiInstanceHelper: MultiInstanceHelper,
     @ShellMainThread private val mainExecutor: ShellExecutor,
     private val desktopTasksLimiter: Optional<DesktopTasksLimiter>,
+    private val recentTasksController: RecentTasksController?
 ) :
     RemoteCallable<DesktopTasksController>,
     Transitions.TransitionHandler,
@@ -293,24 +295,49 @@
         taskId: Int,
         wct: WindowContainerTransaction = WindowContainerTransaction()
     ): Boolean {
-        shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task -> moveToDesktop(task, wct) }
-            ?: return false
+        shellTaskOrganizer.getRunningTaskInfo(taskId)?.let {
+            moveToDesktop(it, wct)
+        } ?: moveToDesktopFromNonRunningTask(taskId, wct)
         return true
     }
 
-    /** Move a task to desktop */
+    private fun moveToDesktopFromNonRunningTask(
+        taskId: Int,
+        wct: WindowContainerTransaction
+    ): Boolean {
+        recentTasksController?.findTaskInBackground(taskId)?.let {
+            KtProtoLog.v(
+                WM_SHELL_DESKTOP_MODE,
+                "DesktopTasksController: moveToDesktopFromNonRunningTask taskId=%d",
+                taskId
+            )
+            // TODO(342378842): Instead of using default display, support multiple displays
+            val taskToMinimize =
+                bringDesktopAppsToFrontBeforeShowingNewTask(DEFAULT_DISPLAY, wct, taskId)
+            addMoveToDesktopChangesNonRunningTask(wct, taskId)
+            // TODO(343149901): Add DPI changes for task launch
+            val transition = enterDesktopTaskTransitionHandler.moveToDesktop(wct)
+            addPendingMinimizeTransition(transition, taskToMinimize)
+            return true
+        } ?: return false
+    }
+
+    private fun addMoveToDesktopChangesNonRunningTask(
+        wct: WindowContainerTransaction,
+        taskId: Int
+    ) {
+        val options = ActivityOptions.makeBasic()
+        options.launchWindowingMode = WINDOWING_MODE_FREEFORM
+        wct.startTask(taskId, options.toBundle())
+    }
+
+    /**
+     * Move a task to desktop
+     */
     fun moveToDesktop(
         task: RunningTaskInfo,
         wct: WindowContainerTransaction = WindowContainerTransaction()
     ) {
-        if (!DesktopModeStatus.canEnterDesktopMode(context)) {
-            KtProtoLog.w(
-                WM_SHELL_DESKTOP_MODE,
-                "DesktopTasksController: Cannot enter desktop, " +
-                    "display does not meet minimum size requirements"
-            )
-            return
-        }
         if (Flags.enableDesktopWindowingModalsPolicy() && isSingleTopActivityTranslucent(task)) {
             KtProtoLog.w(
                 WM_SHELL_DESKTOP_MODE,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index c79eef7e..cd478e5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -143,6 +143,10 @@
     }
 
     public static boolean handles(TransitionInfo info) {
+        // There is no animation for screen-wake unless we are immediately unlocking.
+        if (info.getType() == WindowManager.TRANSIT_WAKE && !info.isKeyguardGoingAway()) {
+            return false;
+        }
         return (info.getFlags() & KEYGUARD_VISIBILITY_TRANSIT_FLAGS) != 0;
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index c7f693d..b52b0d8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -489,6 +489,14 @@
                     // activity windowing mode, and set the task bounds to the final bounds
                     wct.setActivityWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED);
                     wct.setBounds(taskInfo.token, destinationBounds);
+                    // If the animation is only used to apply destination bounds immediately and
+                    // invisibly, then reshow it until the pip is drawn with the bounds.
+                    final PipAnimationController.PipTransitionAnimator<?> animator =
+                            mPipAnimationController.getCurrentAnimator();
+                    if (animator != null && animator.getEndValue().equals(0f)) {
+                        tx.addTransactionCommittedListener(mTransitions.getMainExecutor(),
+                                () -> fadeExistingPip(true /* show */));
+                    }
                 } else {
                     wct.setBounds(taskInfo.token, null /* bounds */);
                 }
@@ -675,6 +683,7 @@
                     .setContainerLayer()
                     .setHidden(false)
                     .setParent(root.getLeash())
+                    .setCallsite("PipTransition.startExitAnimation")
                     .build();
             startTransaction.reparent(activitySurface, pipLeash);
             // Put the activity at local position with offset in case it is letterboxed.
@@ -1025,6 +1034,7 @@
         }
         startTransaction.apply();
 
+        int animationDuration = mEnterExitAnimationDuration;
         PipAnimationController.PipTransitionAnimator animator;
         if (enterAnimationType == ANIM_TYPE_BOUNDS) {
             animator = mPipAnimationController.getAnimator(taskInfo, leash, currentBounds,
@@ -1056,8 +1066,17 @@
                 }
             }
         } else if (enterAnimationType == ANIM_TYPE_ALPHA) {
+            // In case augmentRequest() is unable to apply the entering bounds (e.g. the request
+            // info only contains display change), keep the animation invisible (alpha 0) and
+            // duration 0 to apply the destination bounds. The actual fade-in animation will be
+            // done in onFinishResize() after the bounds are applied.
+            final boolean fadeInAfterOnFinishResize = rotationDelta != Surface.ROTATION_0
+                    && mFixedRotationState == FIXED_ROTATION_CALLBACK;
             animator = mPipAnimationController.getAnimator(taskInfo, leash, destinationBounds,
-                    0f, 1f);
+                    0f, fadeInAfterOnFinishResize ? 0f : 1f);
+            if (fadeInAfterOnFinishResize) {
+                animationDuration = 0;
+            }
             mSurfaceTransactionHelper
                     .crop(finishTransaction, leash, destinationBounds)
                     .round(finishTransaction, leash, true /* applyCornerRadius */);
@@ -1067,7 +1086,7 @@
         mPipOrganizer.setContentOverlay(animator.getContentOverlayLeash(), currentBounds);
         animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
                 .setPipAnimationCallback(mPipAnimationCallback)
-                .setDuration(mEnterExitAnimationDuration);
+                .setDuration(animationDuration);
         if (rotationDelta != Surface.ROTATION_0
                 && mFixedRotationState == FIXED_ROTATION_TRANSITION) {
             // For fixed rotation, the animation destination bounds is in old rotation coordinates.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
index c2f4d72a..ca0d61f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTransition.java
@@ -233,6 +233,7 @@
                                 .setContainerLayer()
                                 .setHidden(false)
                                 .setParent(root.getLeash())
+                                .setCallsite("TvPipTransition.startAnimation")
                                 .build();
                         startTransaction.reparent(activitySurface, pipLeash);
                         // Put the activity at local position with offset in case it is letterboxed.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
new file mode 100644
index 0000000..3e298e5
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/animation/PipAlphaAnimator.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.pip2.animation;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.annotation.IntDef;
+import android.view.SurfaceControl;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.pip2.PipSurfaceTransactionHelper;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Animator that handles the alpha animation for entering PIP
+ */
+public class PipAlphaAnimator extends ValueAnimator implements ValueAnimator.AnimatorUpdateListener,
+        ValueAnimator.AnimatorListener {
+    @IntDef(prefix = {"FADE_"}, value = {
+            FADE_IN,
+            FADE_OUT
+    })
+
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Fade {}
+
+    public static final int FADE_IN = 0;
+    public static final int FADE_OUT = 1;
+
+    private final SurfaceControl mLeash;
+    private final SurfaceControl.Transaction mStartTransaction;
+
+    // optional callbacks for tracking animation start and end
+    @Nullable private Runnable mAnimationStartCallback;
+    @Nullable private Runnable mAnimationEndCallback;
+
+    private final PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
+            mSurfaceControlTransactionFactory;
+
+    public PipAlphaAnimator(SurfaceControl leash,
+            SurfaceControl.Transaction tx,
+            @Fade int direction) {
+        mLeash = leash;
+        mStartTransaction = tx;
+        if (direction == FADE_IN) {
+            setFloatValues(0f, 1f);
+        } else { // direction == FADE_OUT
+            setFloatValues(1f, 0f);
+        }
+        mSurfaceControlTransactionFactory =
+                new PipSurfaceTransactionHelper.VsyncSurfaceControlTransactionFactory();
+        addListener(this);
+        addUpdateListener(this);
+    }
+
+    public void setAnimationStartCallback(@NonNull Runnable runnable) {
+        mAnimationStartCallback = runnable;
+    }
+
+    public void setAnimationEndCallback(@NonNull Runnable runnable) {
+        mAnimationEndCallback = runnable;
+    }
+
+    @Override
+    public void onAnimationStart(@NonNull Animator animation) {
+        if (mAnimationStartCallback != null) {
+            mAnimationStartCallback.run();
+        }
+        if (mStartTransaction != null) {
+            mStartTransaction.apply();
+        }
+    }
+
+    @Override
+    public void onAnimationUpdate(@NonNull ValueAnimator animation) {
+        final float alpha = (Float) animation.getAnimatedValue();
+        mSurfaceControlTransactionFactory.getTransaction().setAlpha(mLeash, alpha).apply();
+    }
+
+    @Override
+    public void onAnimationEnd(@NonNull Animator animation) {
+        if (mAnimationEndCallback != null) {
+            mAnimationEndCallback.run();
+        }
+    }
+
+    @Override
+    public void onAnimationCancel(@NonNull Animator animation) {}
+
+    @Override
+    public void onAnimationRepeat(@NonNull Animator animation) {}
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
index 7dddd27..3e215d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java
@@ -52,6 +52,7 @@
 import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.pip.PipContentOverlay;
 import com.android.wm.shell.pip.PipTransitionController;
+import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
 
@@ -392,8 +393,14 @@
         // cache the PiP task token and leash
         WindowContainerToken pipTaskToken = pipChange.getContainer();
 
-        startTransaction.apply();
-        finishCallback.onTransitionFinished(null);
+        Preconditions.checkNotNull(mPipLeash, "Leash is null for alpha transition.");
+        // start transition with 0 alpha
+        startTransaction.setAlpha(mPipLeash, 0f);
+        PipAlphaAnimator animator = new PipAlphaAnimator(mPipLeash,
+                startTransaction, PipAlphaAnimator.FADE_IN);
+        animator.setAnimationEndCallback(() -> finishCallback.onTransitionFinished(null));
+
+        animator.start();
         return true;
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index d8f2c02..863202d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -446,6 +446,25 @@
         return null;
     }
 
+    /**
+     * Find the background task that match the given taskId.
+     */
+    @Nullable
+    public ActivityManager.RecentTaskInfo findTaskInBackground(int taskId) {
+        List<ActivityManager.RecentTaskInfo> tasks = mActivityTaskManager.getRecentTasks(
+                Integer.MAX_VALUE, ActivityManager.RECENT_IGNORE_UNAVAILABLE,
+                ActivityManager.getCurrentUser());
+        for (int i = 0; i < tasks.size(); i++) {
+            final ActivityManager.RecentTaskInfo task = tasks.get(i);
+            if (task.isVisible) {
+                continue;
+            }
+            if (taskId == task.taskId) {
+                return task;
+            }
+        }
+        return null;
+    }
     public void dump(@NonNull PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
index 11aa402..a126cbe 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTaskController.java
@@ -30,6 +30,7 @@
 import android.content.pm.LauncherApps;
 import android.content.pm.ShortcutInfo;
 import android.graphics.Rect;
+import android.gui.TrustedOverlay;
 import android.os.Binder;
 import android.util.CloseGuard;
 import android.util.Slog;
@@ -448,6 +449,14 @@
         mSurfaceCreated = true;
         mIsInitialized = true;
         mSurfaceControl = surfaceControl;
+        // SurfaceControl is expected to be null only in the case of unit tests. Guard against it
+        // to avoid runtime exception in SurfaceControl.Transaction.
+        if (surfaceControl != null) {
+            // TaskView is meant to contain app activities which shouldn't have trusted overlays
+            // flag set even when itself reparented in a window which is trusted.
+            mTransaction.setTrustedOverlay(surfaceControl, TrustedOverlay.DISABLED)
+                    .apply();
+        }
         notifyInitialized();
         mShellExecutor.execute(() -> {
             if (mTaskToken == null) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
index 9ce2209..e196254 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -167,7 +167,7 @@
                 t.show(mScreenshotLayer);
                 if (!isCustomRotate()) {
                     mStartLuma = TransitionAnimation.getBorderLuma(hardwareBuffer,
-                            screenshotBuffer.getColorSpace());
+                            screenshotBuffer.getColorSpace(), mSurfaceControl);
                 }
                 hardwareBuffer.close();
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
index 1be85d0..ad4f02d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
@@ -280,6 +280,7 @@
                 .setParent(rootLeash)
                 .setColorLayer()
                 .setOpaque(true)
+                .setCallsite("TransitionAnimationHelper.addBackgroundToTransition")
                 .build();
         startTransaction
                 .setLayer(animationBackgroundSurface, Integer.MIN_VALUE)
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 6224543..6ade81c 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
@@ -1591,7 +1591,7 @@
         public void setHomeTransitionListener(IHomeTransitionListener listener) {
             executeRemoteCallWithTaskPermission(mTransitions, "setHomeTransitionListener",
                     (transitions) -> {
-                        transitions.mHomeTransitionObserver.setHomeTransitionListener(mTransitions,
+                        transitions.mHomeTransitionObserver.setHomeTransitionListener(transitions,
                                 listener);
                     });
         }
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 e85cb64..95e0d79 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
@@ -255,6 +255,7 @@
         private final WindowContainerToken mTaskToken;
         private final DragPositioningCallback mDragPositioningCallback;
         private final DragDetector mDragDetector;
+        private final int mDisplayId;
 
         private int mDragPointerId = -1;
         private boolean mIsDragging;
@@ -266,6 +267,7 @@
             mTaskToken = taskInfo.token;
             mDragPositioningCallback = dragPositioningCallback;
             mDragDetector = new DragDetector(this);
+            mDisplayId = taskInfo.displayId;
         }
 
         @Override
@@ -274,7 +276,7 @@
             if (id == R.id.close_window) {
                 mTaskOperations.closeTask(mTaskToken);
             } else if (id == R.id.back_button) {
-                mTaskOperations.injectBackKey();
+                mTaskOperations.injectBackKey(mDisplayId);
             } else if (id == R.id.minimize_window) {
                 mTaskOperations.minimizeTask(mTaskToken);
             } else if (id == R.id.maximize_window) {
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 74b091f..37cdbb4 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
@@ -386,6 +386,7 @@
         private final DragPositioningCallback mDragPositioningCallback;
         private final DragDetector mDragDetector;
         private final GestureDetector mGestureDetector;
+        private final int mDisplayId;
 
         /**
          * Whether to pilfer the next motion event to send cancellations to the windows below.
@@ -407,6 +408,7 @@
             mDragPositioningCallback = dragPositioningCallback;
             mDragDetector = new DragDetector(this);
             mGestureDetector = new GestureDetector(mContext, this);
+            mDisplayId = taskInfo.displayId;
             mCloseMaximizeWindowRunnable = () -> {
                 final DesktopModeWindowDecoration decoration = mWindowDecorByTaskId.get(mTaskId);
                 if (decoration == null) return;
@@ -432,7 +434,7 @@
                     mTaskOperations.closeTask(mTaskToken, wct);
                 }
             } else if (id == R.id.back_button) {
-                mTaskOperations.injectBackKey();
+                mTaskOperations.injectBackKey(mDisplayId);
             } else if (id == R.id.caption_handle || id == R.id.open_menu_button) {
                 if (!decoration.isHandleMenuActive()) {
                     moveTaskToFront(decoration.mTaskInfo);
@@ -581,17 +583,20 @@
             } else if (ev.getAction() == ACTION_HOVER_MOVE
                     && MaximizeMenu.Companion.isMaximizeMenuView(id)) {
                 decoration.onMaximizeMenuHoverMove(id, ev);
+                mMainHandler.removeCallbacks(mCloseMaximizeWindowRunnable);
             } else if (ev.getAction() == ACTION_HOVER_EXIT) {
                 if (!decoration.isMaximizeMenuActive() && id == R.id.maximize_window) {
                     decoration.onMaximizeWindowHoverExit();
-                } else if (id == R.id.maximize_window || id == R.id.maximize_menu) {
+                } else if (id == R.id.maximize_window
+                        || MaximizeMenu.Companion.isMaximizeMenuView(id)) {
                     // Close menu if not hovering over maximize menu or maximize button after a
                     // delay to give user a chance to re-enter view or to move from one maximize
                     // menu view to another.
                     mMainHandler.postDelayed(mCloseMaximizeWindowRunnable,
                             CLOSE_MAXIMIZE_MENU_DELAY_MS);
-                } else if (MaximizeMenu.Companion.isMaximizeMenuView(id)) {
-                    decoration.onMaximizeMenuHoverExit(id, ev);
+                    if (id != R.id.maximize_window) {
+                        decoration.onMaximizeMenuHoverExit(id, ev);
+                    }
                 }
                 return true;
             }
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 5379ca6..badce6e 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
@@ -132,6 +132,7 @@
                 .setName("TaskInputSink of " + decorationSurface)
                 .setContainerLayer()
                 .setParent(mDecorationSurface)
+                .setCallsite("DragResizeInputListener.constructor")
                 .build();
         mSurfaceControlTransactionSupplier.get()
                 .setLayer(mInputSinkSurface, WindowDecoration.INPUT_SINK_Z_ORDER)
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
index 78f0ef7..4f04901 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeButtonView.kt
@@ -88,7 +88,7 @@
     }
 
     fun cancelHoverAnimation() {
-        hoverProgressAnimatorSet.removeAllListeners()
+        hoverProgressAnimatorSet.childAnimations.forEach { it.removeAllListeners() }
         hoverProgressAnimatorSet.cancel()
         progressBar.visibility = View.INVISIBLE
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
deleted file mode 100644
index 93e2a21..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.java
+++ /dev/null
@@ -1,451 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.windowdecor;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
-import android.annotation.ColorRes;
-import android.annotation.NonNull;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Bitmap;
-import android.graphics.Color;
-import android.graphics.PixelFormat;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.os.Trace;
-import android.view.Display;
-import android.view.LayoutInflater;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.SurfaceSession;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowlessWindowManager;
-import android.widget.ImageView;
-import android.window.TaskConstants;
-
-import com.android.wm.shell.R;
-import com.android.wm.shell.common.DisplayController;
-
-import java.util.function.Supplier;
-
-/**
- * Creates and updates a veil that covers task contents on resize.
- */
-public class ResizeVeil {
-    private static final String TAG = "ResizeVeil";
-    private static final int RESIZE_ALPHA_DURATION = 100;
-
-    private static final int VEIL_CONTAINER_LAYER = TaskConstants.TASK_CHILD_LAYER_RESIZE_VEIL;
-    /** The background is a child of the veil container layer and goes at the bottom. */
-    private static final int VEIL_BACKGROUND_LAYER = 0;
-    /** The icon is a child of the veil container layer and goes in front of the background. */
-    private static final int VEIL_ICON_LAYER = 1;
-
-    private final Context mContext;
-    private final DisplayController mDisplayController;
-    private final Supplier<SurfaceControl.Transaction> mSurfaceControlTransactionSupplier;
-    private final SurfaceControlBuilderFactory mSurfaceControlBuilderFactory;
-    private final WindowDecoration.SurfaceControlViewHostFactory mSurfaceControlViewHostFactory;
-    private final SurfaceSession mSurfaceSession = new SurfaceSession();
-    private final Bitmap mAppIcon;
-    private ImageView mIconView;
-    private int mIconSize;
-    private SurfaceControl mParentSurface;
-
-    /** A container surface to host the veil background and icon child surfaces. */
-    private SurfaceControl mVeilSurface;
-    /** A color surface for the veil background. */
-    private SurfaceControl mBackgroundSurface;
-    /** A surface that hosts a windowless window with the app icon. */
-    private SurfaceControl mIconSurface;
-
-    private final RunningTaskInfo mTaskInfo;
-    private SurfaceControlViewHost mViewHost;
-    private Display mDisplay;
-    private ValueAnimator mVeilAnimator;
-
-    private boolean mIsShowing = false;
-
-    private final DisplayController.OnDisplaysChangedListener mOnDisplaysChangedListener =
-            new DisplayController.OnDisplaysChangedListener() {
-                @Override
-                public void onDisplayAdded(int displayId) {
-                    if (mTaskInfo.displayId != displayId) {
-                        return;
-                    }
-                    mDisplayController.removeDisplayWindowListener(this);
-                    setupResizeVeil();
-                }
-            };
-
-    public ResizeVeil(Context context,
-            @NonNull DisplayController displayController,
-            Bitmap appIcon, RunningTaskInfo taskInfo,
-            SurfaceControl taskSurface,
-            Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier) {
-        this(context,
-                displayController,
-                appIcon,
-                taskInfo,
-                taskSurface,
-                surfaceControlTransactionSupplier,
-                new SurfaceControlBuilderFactory() {},
-                new WindowDecoration.SurfaceControlViewHostFactory() {});
-    }
-
-    public ResizeVeil(Context context,
-            @NonNull DisplayController displayController,
-            Bitmap appIcon, RunningTaskInfo taskInfo,
-            SurfaceControl taskSurface,
-            Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
-            SurfaceControlBuilderFactory surfaceControlBuilderFactory,
-            WindowDecoration.SurfaceControlViewHostFactory surfaceControlViewHostFactory) {
-        mContext = context;
-        mDisplayController = displayController;
-        mAppIcon = appIcon;
-        mSurfaceControlTransactionSupplier = surfaceControlTransactionSupplier;
-        mTaskInfo = taskInfo;
-        mParentSurface = taskSurface;
-        mSurfaceControlBuilderFactory = surfaceControlBuilderFactory;
-        mSurfaceControlViewHostFactory = surfaceControlViewHostFactory;
-        setupResizeVeil();
-    }
-    /**
-     * Create the veil in its default invisible state.
-     */
-    private void setupResizeVeil() {
-        if (!obtainDisplayOrRegisterListener()) {
-            // Display may not be available yet, skip this until then.
-            return;
-        }
-        Trace.beginSection("ResizeVeil#setupResizeVeil");
-        mVeilSurface = mSurfaceControlBuilderFactory
-                .create("Resize veil of Task=" + mTaskInfo.taskId)
-                .setContainerLayer()
-                .setHidden(true)
-                .setParent(mParentSurface)
-                .setCallsite("ResizeVeil#setupResizeVeil")
-                .build();
-        mBackgroundSurface = mSurfaceControlBuilderFactory
-                .create("Resize veil background of Task=" + mTaskInfo.taskId, mSurfaceSession)
-                .setColorLayer()
-                .setHidden(true)
-                .setParent(mVeilSurface)
-                .setCallsite("ResizeVeil#setupResizeVeil")
-                .build();
-        mIconSurface = mSurfaceControlBuilderFactory
-                .create("Resize veil icon of Task=" + mTaskInfo.taskId)
-                .setContainerLayer()
-                .setHidden(true)
-                .setParent(mVeilSurface)
-                .setCallsite("ResizeVeil#setupResizeVeil")
-                .build();
-
-        mIconSize = mContext.getResources()
-                .getDimensionPixelSize(R.dimen.desktop_mode_resize_veil_icon_size);
-        final View root = LayoutInflater.from(mContext)
-                .inflate(R.layout.desktop_mode_resize_veil, null /* root */);
-        mIconView = root.findViewById(R.id.veil_application_icon);
-        mIconView.setImageBitmap(mAppIcon);
-
-        final WindowManager.LayoutParams lp =
-                new WindowManager.LayoutParams(
-                        mIconSize,
-                        mIconSize,
-                        WindowManager.LayoutParams.TYPE_APPLICATION,
-                        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
-                        PixelFormat.TRANSPARENT);
-        lp.setTitle("Resize veil icon window of Task=" + mTaskInfo.taskId);
-        lp.setTrustedOverlay();
-
-        final WindowlessWindowManager wwm = new WindowlessWindowManager(mTaskInfo.configuration,
-                mIconSurface, null /* hostInputToken */);
-
-        mViewHost = mSurfaceControlViewHostFactory.create(mContext, mDisplay, wwm, "ResizeVeil");
-        mViewHost.setView(root, lp);
-        Trace.endSection();
-    }
-
-    private boolean obtainDisplayOrRegisterListener() {
-        mDisplay = mDisplayController.getDisplay(mTaskInfo.displayId);
-        if (mDisplay == null) {
-            mDisplayController.addDisplayWindowListener(mOnDisplaysChangedListener);
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Shows the veil surface/view.
-     *
-     * @param t the transaction to apply in sync with the veil draw
-     * @param parentSurface the surface that the veil should be a child of
-     * @param taskBounds the bounds of the task that owns the veil
-     * @param fadeIn if true, the veil will fade-in with an animation, if false, it will be shown
-     *               immediately
-     */
-    public void showVeil(SurfaceControl.Transaction t, SurfaceControl parentSurface,
-            Rect taskBounds, boolean fadeIn) {
-        if (!isReady() || isVisible()) {
-            t.apply();
-            return;
-        }
-        mIsShowing = true;
-
-        // Parent surface can change, ensure it is up to date.
-        if (!parentSurface.equals(mParentSurface)) {
-            t.reparent(mVeilSurface, parentSurface);
-            mParentSurface = parentSurface;
-        }
-
-        t.show(mVeilSurface);
-        t.setLayer(mVeilSurface, VEIL_CONTAINER_LAYER);
-        t.setLayer(mIconSurface, VEIL_ICON_LAYER);
-        t.setLayer(mBackgroundSurface, VEIL_BACKGROUND_LAYER);
-        t.setColor(mBackgroundSurface,
-                Color.valueOf(mContext.getColor(getBackgroundColorId())).getComponents());
-
-        relayout(taskBounds, t);
-        if (fadeIn) {
-            cancelAnimation();
-            final SurfaceControl.Transaction veilAnimT = mSurfaceControlTransactionSupplier.get();
-            mVeilAnimator = new ValueAnimator();
-            mVeilAnimator.setFloatValues(0f, 1f);
-            mVeilAnimator.setDuration(RESIZE_ALPHA_DURATION);
-            mVeilAnimator.addUpdateListener(animation -> {
-                veilAnimT.setAlpha(mBackgroundSurface, mVeilAnimator.getAnimatedFraction());
-                veilAnimT.apply();
-            });
-            mVeilAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    veilAnimT.show(mBackgroundSurface)
-                            .setAlpha(mBackgroundSurface, 0)
-                            .apply();
-                }
-
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    veilAnimT.setAlpha(mBackgroundSurface, 1).apply();
-                }
-            });
-
-            final SurfaceControl.Transaction iconAnimT = mSurfaceControlTransactionSupplier.get();
-            final ValueAnimator iconAnimator = new ValueAnimator();
-            iconAnimator.setFloatValues(0f, 1f);
-            iconAnimator.setDuration(RESIZE_ALPHA_DURATION);
-            iconAnimator.addUpdateListener(animation -> {
-                iconAnimT.setAlpha(mIconSurface, animation.getAnimatedFraction());
-                iconAnimT.apply();
-            });
-            iconAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                    iconAnimT.show(mIconSurface)
-                            .setAlpha(mIconSurface, 0)
-                            .apply();
-                }
-
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    iconAnimT.setAlpha(mIconSurface, 1).apply();
-                }
-            });
-            // Let the animators show it with the correct alpha value once the animation starts.
-            t.hide(mIconSurface);
-            t.hide(mBackgroundSurface);
-            t.apply();
-
-            mVeilAnimator.start();
-            iconAnimator.start();
-        } else {
-            // Show the veil immediately.
-            t.show(mIconSurface);
-            t.show(mBackgroundSurface);
-            t.setAlpha(mIconSurface, 1);
-            t.setAlpha(mBackgroundSurface, 1);
-            t.apply();
-        }
-    }
-
-    /**
-     * Animate veil's alpha to 1, fading it in.
-     */
-    public void showVeil(SurfaceControl parentSurface, Rect taskBounds) {
-        if (!isReady() || isVisible()) {
-            return;
-        }
-        SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
-        showVeil(t, parentSurface, taskBounds, true /* fadeIn */);
-    }
-
-    /**
-     * Update veil bounds to match bounds changes.
-     * @param newBounds bounds to update veil to.
-     */
-    private void relayout(Rect newBounds, SurfaceControl.Transaction t) {
-        t.setWindowCrop(mVeilSurface, newBounds.width(), newBounds.height());
-        final PointF iconPosition = calculateAppIconPosition(newBounds);
-        t.setPosition(mIconSurface, iconPosition.x, iconPosition.y);
-        t.setPosition(mParentSurface, newBounds.left, newBounds.top);
-        t.setWindowCrop(mParentSurface, newBounds.width(), newBounds.height());
-    }
-
-    /**
-     * Calls relayout to update task and veil bounds.
-     * @param newBounds bounds to update veil to.
-     */
-    public void updateResizeVeil(Rect newBounds) {
-        if (!isVisible()) {
-            return;
-        }
-        SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
-        updateResizeVeil(t, newBounds);
-    }
-
-    /**
-     * Calls relayout to update task and veil bounds.
-     * Finishes veil fade in if animation is currently running; this is to prevent empty space
-     * being visible behind the transparent veil during a fast resize.
-     *
-     * @param t a transaction to be applied in sync with the veil draw.
-     * @param newBounds bounds to update veil to.
-     */
-    public void updateResizeVeil(SurfaceControl.Transaction t, Rect newBounds) {
-        if (!isVisible()) {
-            t.apply();
-            return;
-        }
-        if (mVeilAnimator != null && mVeilAnimator.isStarted()) {
-            mVeilAnimator.removeAllUpdateListeners();
-            mVeilAnimator.end();
-        }
-        relayout(newBounds, t);
-        t.apply();
-    }
-
-    /**
-     * Animate veil's alpha to 0, fading it out.
-     */
-    public void hideVeil() {
-        if (!isVisible()) {
-            return;
-        }
-        cancelAnimation();
-        mVeilAnimator = new ValueAnimator();
-        mVeilAnimator.setFloatValues(1, 0);
-        mVeilAnimator.setDuration(RESIZE_ALPHA_DURATION);
-        mVeilAnimator.addUpdateListener(animation -> {
-            SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
-            t.setAlpha(mBackgroundSurface, 1 - mVeilAnimator.getAnimatedFraction());
-            t.setAlpha(mIconSurface, 1 - mVeilAnimator.getAnimatedFraction());
-            t.apply();
-        });
-        mVeilAnimator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
-                t.hide(mBackgroundSurface);
-                t.hide(mIconSurface);
-                t.apply();
-            }
-        });
-        mVeilAnimator.start();
-        mIsShowing = false;
-    }
-
-    @ColorRes
-    private int getBackgroundColorId() {
-        Configuration configuration = mContext.getResources().getConfiguration();
-        if ((configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK)
-                == Configuration.UI_MODE_NIGHT_YES) {
-            return R.color.desktop_mode_resize_veil_dark;
-        } else {
-            return R.color.desktop_mode_resize_veil_light;
-        }
-    }
-
-    private PointF calculateAppIconPosition(Rect parentBounds) {
-        return new PointF((float) parentBounds.width() / 2 - (float) mIconSize / 2,
-                (float) parentBounds.height() / 2 - (float) mIconSize / 2);
-    }
-
-    private void cancelAnimation() {
-        if (mVeilAnimator != null) {
-            mVeilAnimator.removeAllUpdateListeners();
-            mVeilAnimator.cancel();
-        }
-    }
-
-    /**
-     * Whether the resize veil is currently visible.
-     *
-     * Note: when animating a {@link ResizeVeil#hideVeil()}, the veil is considered visible as soon
-     * as the animation starts.
-     */
-    private boolean isVisible() {
-        return mIsShowing;
-    }
-
-    /** Whether the resize veil is ready to be shown. */
-    private boolean isReady() {
-        return mViewHost != null;
-    }
-
-    /**
-     * Dispose of veil when it is no longer needed, likely on close of its container decor.
-     */
-    void dispose() {
-        cancelAnimation();
-        mIsShowing = false;
-        mVeilAnimator = null;
-
-        if (mViewHost != null) {
-            mViewHost.release();
-            mViewHost = null;
-        }
-        final SurfaceControl.Transaction t = mSurfaceControlTransactionSupplier.get();
-        if (mBackgroundSurface != null) {
-            t.remove(mBackgroundSurface);
-            mBackgroundSurface = null;
-        }
-        if (mIconSurface != null) {
-            t.remove(mIconSurface);
-            mIconSurface = null;
-        }
-        if (mVeilSurface != null) {
-            t.remove(mVeilSurface);
-            mVeilSurface = null;
-        }
-        t.apply();
-        mDisplayController.removeDisplayWindowListener(mOnDisplaysChangedListener);
-    }
-
-    interface SurfaceControlBuilderFactory {
-        default SurfaceControl.Builder create(@NonNull String name) {
-            return new SurfaceControl.Builder().setName(name);
-        }
-        default SurfaceControl.Builder create(@NonNull String name,
-                @NonNull SurfaceSession surfaceSession) {
-            return new SurfaceControl.Builder(surfaceSession).setName(name);
-        }
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt
new file mode 100644
index 0000000..4f2d945
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/ResizeVeil.kt
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.windowdecor
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.Bitmap
+import android.graphics.Color
+import android.graphics.PixelFormat
+import android.graphics.PointF
+import android.graphics.Rect
+import android.os.Trace
+import android.view.Display
+import android.view.LayoutInflater
+import android.view.SurfaceControl
+import android.view.SurfaceControlViewHost
+import android.view.SurfaceSession
+import android.view.WindowManager
+import android.view.WindowlessWindowManager
+import android.widget.ImageView
+import android.window.TaskConstants
+import com.android.wm.shell.R
+import com.android.wm.shell.common.DisplayController
+import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener
+import com.android.wm.shell.windowdecor.WindowDecoration.SurfaceControlViewHostFactory
+import java.util.function.Supplier
+
+/**
+ * Creates and updates a veil that covers task contents on resize.
+ */
+class ResizeVeil @JvmOverloads constructor(
+        private val context: Context,
+        private val displayController: DisplayController,
+        private val appIcon: Bitmap,
+        private val taskInfo: RunningTaskInfo,
+        private var parentSurface: SurfaceControl,
+        private val surfaceControlTransactionSupplier: Supplier<SurfaceControl.Transaction>,
+        private val surfaceControlBuilderFactory: SurfaceControlBuilderFactory =
+                object : SurfaceControlBuilderFactory {},
+        private val surfaceControlViewHostFactory: SurfaceControlViewHostFactory =
+                object : SurfaceControlViewHostFactory {}
+) {
+    private val surfaceSession = SurfaceSession()
+    private lateinit var iconView: ImageView
+    private var iconSize = 0
+
+    /** A container surface to host the veil background and icon child surfaces.  */
+    private var veilSurface: SurfaceControl? = null
+    /** A color surface for the veil background.  */
+    private var backgroundSurface: SurfaceControl? = null
+    /** A surface that hosts a windowless window with the app icon.  */
+    private var iconSurface: SurfaceControl? = null
+    private var viewHost: SurfaceControlViewHost? = null
+    private var display: Display? = null
+    private var veilAnimator: ValueAnimator? = null
+
+    /**
+     * Whether the resize veil is currently visible.
+     *
+     * Note: when animating a [ResizeVeil.hideVeil], the veil is considered visible as soon
+     * as the animation starts.
+     */
+    private var isVisible = false
+
+    private val onDisplaysChangedListener: OnDisplaysChangedListener =
+            object : OnDisplaysChangedListener {
+                override fun onDisplayAdded(displayId: Int) {
+                    if (taskInfo.displayId != displayId) {
+                        return
+                    }
+                    displayController.removeDisplayWindowListener(this)
+                    setupResizeVeil()
+                }
+            }
+
+    private val backgroundColorId: Int
+        get() {
+            val configuration = context.resources.configuration
+            return if (configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
+                    == Configuration.UI_MODE_NIGHT_YES) {
+                R.color.desktop_mode_resize_veil_dark
+            } else {
+                R.color.desktop_mode_resize_veil_light
+            }
+        }
+
+    /**
+     * Whether the resize veil is ready to be shown.
+     */
+    private val isReady: Boolean
+        get() = viewHost != null
+
+    init {
+        setupResizeVeil()
+    }
+
+    /**
+     * Create the veil in its default invisible state.
+     */
+    private fun setupResizeVeil() {
+        if (!obtainDisplayOrRegisterListener()) {
+            // Display may not be available yet, skip this until then.
+            return
+        }
+        Trace.beginSection("ResizeVeil#setupResizeVeil")
+        veilSurface = surfaceControlBuilderFactory
+                .create("Resize veil of Task=" + taskInfo.taskId)
+                .setContainerLayer()
+                .setHidden(true)
+                .setParent(parentSurface)
+                .setCallsite("ResizeVeil#setupResizeVeil")
+                .build()
+        backgroundSurface = surfaceControlBuilderFactory
+                .create("Resize veil background of Task=" + taskInfo.taskId, surfaceSession)
+                .setColorLayer()
+                .setHidden(true)
+                .setParent(veilSurface)
+                .setCallsite("ResizeVeil#setupResizeVeil")
+                .build()
+        iconSurface = surfaceControlBuilderFactory
+                .create("Resize veil icon of Task=" + taskInfo.taskId)
+                .setContainerLayer()
+                .setHidden(true)
+                .setParent(veilSurface)
+                .setCallsite("ResizeVeil#setupResizeVeil")
+                .build()
+        iconSize = context.resources
+                .getDimensionPixelSize(R.dimen.desktop_mode_resize_veil_icon_size)
+        val root = LayoutInflater.from(context)
+                .inflate(R.layout.desktop_mode_resize_veil, null /* root */)
+        iconView = root.requireViewById(R.id.veil_application_icon)
+        iconView.setImageBitmap(appIcon)
+        val lp = WindowManager.LayoutParams(
+                iconSize,
+                iconSize,
+                WindowManager.LayoutParams.TYPE_APPLICATION,
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+                PixelFormat.TRANSPARENT)
+        lp.title = "Resize veil icon window of Task=" + taskInfo.taskId
+        lp.setTrustedOverlay()
+        val wwm = WindowlessWindowManager(taskInfo.configuration,
+                iconSurface, null /* hostInputToken */)
+        viewHost = surfaceControlViewHostFactory.create(context, display, wwm, "ResizeVeil")
+        viewHost?.setView(root, lp)
+        Trace.endSection()
+    }
+
+    private fun obtainDisplayOrRegisterListener(): Boolean {
+        display = displayController.getDisplay(taskInfo.displayId)
+        if (display == null) {
+            displayController.addDisplayWindowListener(onDisplaysChangedListener)
+            return false
+        }
+        return true
+    }
+
+    /**
+     * Shows the veil surface/view.
+     *
+     * @param t the transaction to apply in sync with the veil draw
+     * @param parent the surface that the veil should be a child of
+     * @param taskBounds the bounds of the task that owns the veil
+     * @param fadeIn if true, the veil will fade-in with an animation, if false, it will be shown
+     * immediately
+     */
+    fun showVeil(
+            t: SurfaceControl.Transaction,
+            parent: SurfaceControl,
+            taskBounds: Rect,
+            fadeIn: Boolean
+    ) {
+        if (!isReady || isVisible) {
+            t.apply()
+            return
+        }
+        isVisible = true
+        val background = backgroundSurface
+        val icon = iconSurface
+        val veil = veilSurface
+        if (background == null || icon == null || veil == null) return
+
+        // Parent surface can change, ensure it is up to date.
+        if (parent != parentSurface) {
+            t.reparent(veil, parent)
+            parentSurface = parent
+        }
+
+
+        t.show(veil)
+                .setLayer(veil, VEIL_CONTAINER_LAYER)
+                .setLayer(icon, VEIL_ICON_LAYER)
+                .setLayer(background, VEIL_BACKGROUND_LAYER)
+                .setColor(background,
+                        Color.valueOf(context.getColor(backgroundColorId)).components)
+        relayout(taskBounds, t)
+        if (fadeIn) {
+            cancelAnimation()
+            val veilAnimT = surfaceControlTransactionSupplier.get()
+            val iconAnimT = surfaceControlTransactionSupplier.get()
+            veilAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
+                duration = RESIZE_ALPHA_DURATION
+                addUpdateListener {
+                    veilAnimT.setAlpha(background, animatedValue as Float)
+                            .apply()
+                }
+                addListener(object : AnimatorListenerAdapter() {
+                    override fun onAnimationStart(animation: Animator) {
+                        veilAnimT.show(background)
+                                .setAlpha(background, 0f)
+                                .apply()
+                    }
+
+                    override fun onAnimationEnd(animation: Animator) {
+                        veilAnimT.setAlpha(background, 1f).apply()
+                    }
+                })
+            }
+            val iconAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
+                duration = RESIZE_ALPHA_DURATION
+                addUpdateListener {
+                    iconAnimT.setAlpha(icon, animatedValue as Float)
+                            .apply()
+                }
+                addListener(object : AnimatorListenerAdapter() {
+                    override fun onAnimationStart(animation: Animator) {
+                        iconAnimT.show(icon)
+                                .setAlpha(icon, 0f)
+                                .apply()
+                    }
+
+                    override fun onAnimationEnd(animation: Animator) {
+                        iconAnimT.setAlpha(icon, 1f).apply()
+                    }
+                })
+            }
+
+            // Let the animators show it with the correct alpha value once the animation starts.
+            t.hide(icon)
+                    .hide(background)
+                    .apply()
+            veilAnimator?.start()
+            iconAnimator.start()
+        } else {
+            // Show the veil immediately.
+            t.show(icon)
+                    .show(background)
+                    .setAlpha(icon, 1f)
+                    .setAlpha(background, 1f)
+                    .apply()
+        }
+    }
+
+    /**
+     * Animate veil's alpha to 1, fading it in.
+     */
+    fun showVeil(parentSurface: SurfaceControl, taskBounds: Rect) {
+        if (!isReady || isVisible) {
+            return
+        }
+        val t = surfaceControlTransactionSupplier.get()
+        showVeil(t, parentSurface, taskBounds, true /* fadeIn */)
+    }
+
+    /**
+     * Update veil bounds to match bounds changes.
+     * @param newBounds bounds to update veil to.
+     */
+    private fun relayout(newBounds: Rect, t: SurfaceControl.Transaction) {
+        val iconPosition = calculateAppIconPosition(newBounds)
+        val veil = veilSurface
+        val icon = iconSurface
+        if (veil == null || icon == null) return
+        t.setWindowCrop(veil, newBounds.width(), newBounds.height())
+                .setPosition(icon, iconPosition.x, iconPosition.y)
+                .setPosition(parentSurface, newBounds.left.toFloat(), newBounds.top.toFloat())
+                .setWindowCrop(parentSurface, newBounds.width(), newBounds.height())
+    }
+
+    /**
+     * Calls relayout to update task and veil bounds.
+     * @param newBounds bounds to update veil to.
+     */
+    fun updateResizeVeil(newBounds: Rect) {
+        if (!isVisible) {
+            return
+        }
+        val t = surfaceControlTransactionSupplier.get()
+        updateResizeVeil(t, newBounds)
+    }
+
+    /**
+     * Calls relayout to update task and veil bounds.
+     * Finishes veil fade in if animation is currently running; this is to prevent empty space
+     * being visible behind the transparent veil during a fast resize.
+     *
+     * @param t a transaction to be applied in sync with the veil draw.
+     * @param newBounds bounds to update veil to.
+     */
+    fun updateResizeVeil(t: SurfaceControl.Transaction, newBounds: Rect) {
+        if (!isVisible) {
+            t.apply()
+            return
+        }
+        veilAnimator?.let { animator ->
+            if (animator.isStarted) {
+                animator.removeAllUpdateListeners()
+                animator.end()
+            }
+        }
+        relayout(newBounds, t)
+        t.apply()
+    }
+
+    /**
+     * Animate veil's alpha to 0, fading it out.
+     */
+    fun hideVeil() {
+        if (!isVisible) {
+            return
+        }
+        cancelAnimation()
+        val background = backgroundSurface
+        val icon = iconSurface
+        if (background == null || icon == null) return
+
+        veilAnimator = ValueAnimator.ofFloat(1f, 0f).apply {
+            duration = RESIZE_ALPHA_DURATION
+            addUpdateListener {
+                surfaceControlTransactionSupplier.get()
+                        .setAlpha(background, animatedValue as Float)
+                        .setAlpha(icon, animatedValue as Float)
+                        .apply()
+            }
+            addListener(object : AnimatorListenerAdapter() {
+                override fun onAnimationEnd(animation: Animator) {
+                    surfaceControlTransactionSupplier.get()
+                            .hide(background)
+                            .hide(icon)
+                            .apply()
+                }
+            })
+        }
+        veilAnimator?.start()
+        isVisible = false
+    }
+
+    private fun calculateAppIconPosition(parentBounds: Rect): PointF {
+        return PointF(parentBounds.width().toFloat() / 2 - iconSize.toFloat() / 2,
+                parentBounds.height().toFloat() / 2 - iconSize.toFloat() / 2)
+    }
+
+    private fun cancelAnimation() {
+        veilAnimator?.removeAllUpdateListeners()
+        veilAnimator?.cancel()
+    }
+
+    /**
+     * Dispose of veil when it is no longer needed, likely on close of its container decor.
+     */
+    fun dispose() {
+        cancelAnimation()
+        veilAnimator = null
+        isVisible = false
+
+        viewHost?.release()
+        viewHost = null
+
+        val t: SurfaceControl.Transaction = surfaceControlTransactionSupplier.get()
+        backgroundSurface?.let { background -> t.remove(background) }
+        backgroundSurface = null
+        iconSurface?.let { icon -> t.remove(icon) }
+        iconSurface = null
+        veilSurface?.let { veil -> t.remove(veil) }
+        veilSurface = null
+        t.apply()
+        displayController.removeDisplayWindowListener(onDisplaysChangedListener)
+    }
+
+    interface SurfaceControlBuilderFactory {
+        fun create(name: String): SurfaceControl.Builder {
+            return SurfaceControl.Builder().setName(name)
+        }
+
+        fun create(name: String, surfaceSession: SurfaceSession): SurfaceControl.Builder {
+            return SurfaceControl.Builder(surfaceSession).setName(name)
+        }
+    }
+
+    companion object {
+        private const val TAG = "ResizeVeil"
+        private const val RESIZE_ALPHA_DURATION = 100L
+        private const val VEIL_CONTAINER_LAYER = TaskConstants.TASK_CHILD_LAYER_RESIZE_VEIL
+
+        /** The background is a child of the veil container layer and goes at the bottom.  */
+        private const val VEIL_BACKGROUND_LAYER = 0
+
+        /** The icon is a child of the veil container layer and goes in front of the background.  */
+        private const val VEIL_ICON_LAYER = 1
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java
index 53d4e27..ad238c3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java
@@ -52,19 +52,19 @@
         mSyncQueue = syncQueue;
     }
 
-    void injectBackKey() {
-        sendBackEvent(KeyEvent.ACTION_DOWN);
-        sendBackEvent(KeyEvent.ACTION_UP);
+    void injectBackKey(int displayId) {
+        sendBackEvent(KeyEvent.ACTION_DOWN, displayId);
+        sendBackEvent(KeyEvent.ACTION_UP, displayId);
     }
 
-    private void sendBackEvent(int action) {
+    private void sendBackEvent(int action, int displayId) {
         final long when = SystemClock.uptimeMillis();
         final KeyEvent ev = new KeyEvent(when, when, action, KeyEvent.KEYCODE_BACK,
                 0 /* repeat */, 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD,
                 0 /* scancode */, KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
                 InputDevice.SOURCE_KEYBOARD);
 
-        ev.setDisplayId(mContext.getDisplay().getDisplayId());
+        ev.setDisplayId(displayId);
         if (!mContext.getSystemService(InputManager.class)
                 .injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC)) {
             Log.e(TAG, "Inject input event fail");
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 2cbe472..2ae3cb9 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
@@ -51,6 +51,7 @@
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.shared.DesktopModeStatus;
@@ -268,6 +269,7 @@
                     .setName("Decor container of Task=" + mTaskInfo.taskId)
                     .setContainerLayer()
                     .setParent(mTaskSurface)
+                    .setCallsite("WindowDecoration.relayout_1")
                     .build();
 
             startT.setTrustedOverlay(mDecorationContainerSurface, true)
@@ -285,6 +287,7 @@
                     .setName("Caption container of Task=" + mTaskInfo.taskId)
                     .setContainerLayer()
                     .setParent(mDecorationContainerSurface)
+                    .setCallsite("WindowDecoration.relayout_2")
                     .build();
         }
 
@@ -575,6 +578,7 @@
                 .setName(namePrefix + " of Task=" + mTaskInfo.taskId)
                 .setContainerLayer()
                 .setParent(mDecorationContainerSurface)
+                .setCallsite("WindowDecoration.addWindow")
                 .build();
         View v = LayoutInflater.from(mDecorWindowContext).inflate(layoutId, null);
 
@@ -684,7 +688,8 @@
         }
     }
 
-    interface SurfaceControlViewHostFactory {
+    @VisibleForTesting
+    public interface SurfaceControlViewHostFactory {
         default SurfaceControlViewHost create(Context c, Display d, WindowlessWindowManager wmm) {
             return new SurfaceControlViewHost(c, d, wmm, "WindowDecoration");
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt
index ec20471..7ade987 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt
@@ -23,13 +23,13 @@
 
 val TaskInfo.isTransparentCaptionBarAppearance: Boolean
     get() {
-        val appearance = taskDescription?.systemBarsAppearance ?: 0
+        val appearance = taskDescription?.topOpaqueSystemBarsAppearance ?: 0
         return (appearance and APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND) != 0
     }
 
 val TaskInfo.isLightCaptionBarAppearance: Boolean
     get() {
-        val appearance = taskDescription?.systemBarsAppearance ?: 0
+        val appearance = taskDescription?.topOpaqueSystemBarsAppearance ?: 0
         return (appearance and APPEARANCE_LIGHT_CAPTION_BARS) != 0
     }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt
index 285e5b6..51b291c0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLoggerTest.kt
@@ -39,7 +39,7 @@
 class DesktopModeUiEventLoggerTest : ShellTestCase() {
     private lateinit var uiEventLoggerFake: UiEventLoggerFake
     private lateinit var logger: DesktopModeUiEventLogger
-    private val instanceIdSequence = InstanceIdSequence(10)
+    private val instanceIdSequence = InstanceIdSequence(/* instanceIdMax */ 1 shl 20)
 
 
     @Before
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 ac67bd1..cf6cea2 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
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.desktopmode
 
+import android.app.ActivityManager.RecentTaskInfo
 import android.app.ActivityManager.RunningTaskInfo
 import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
 import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
@@ -47,13 +48,16 @@
 import android.view.WindowManager.TRANSIT_TO_BACK
 import android.view.WindowManager.TRANSIT_TO_FRONT
 import android.window.DisplayAreaInfo
+import android.window.IWindowContainerToken
 import android.window.RemoteTransition
 import android.window.TransitionRequestInfo
 import android.window.WindowContainerToken
 import android.window.WindowContainerTransaction
+import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK
 import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT
 import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK
 import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER
+import android.window.WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID
 import androidx.test.filters.SmallTest
 import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
@@ -78,6 +82,7 @@
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask
 import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createSplitScreenTask
 import com.android.wm.shell.draganddrop.DragAndDropController
+import com.android.wm.shell.recents.RecentTasksController
 import com.android.wm.shell.recents.RecentsTransitionHandler
 import com.android.wm.shell.recents.RecentsTransitionStateListener
 import com.android.wm.shell.shared.DesktopModeStatus
@@ -93,6 +98,7 @@
 import com.android.wm.shell.transition.Transitions.TransitionHandler
 import com.google.common.truth.Truth.assertThat
 import com.google.common.truth.Truth.assertWithMessage
+import java.util.Optional
 import org.junit.After
 import org.junit.Assume.assumeTrue
 import org.junit.Before
@@ -115,7 +121,6 @@
 import org.mockito.kotlin.atLeastOnce
 import org.mockito.kotlin.capture
 import org.mockito.quality.Strictness
-import java.util.Optional
 import junit.framework.Assert.assertFalse
 import junit.framework.Assert.assertTrue
 import org.mockito.Mockito.`when` as whenever
@@ -154,6 +159,7 @@
     @Mock lateinit var multiInstanceHelper: MultiInstanceHelper
     @Mock lateinit var desktopModeLoggerTransitionObserver: DesktopModeLoggerTransitionObserver
     @Mock lateinit var desktopModeVisualIndicator: DesktopModeVisualIndicator
+    @Mock lateinit var recentTasksController: RecentTasksController
 
     private lateinit var mockitoSession: StaticMockitoSession
     private lateinit var controller: DesktopTasksController
@@ -233,6 +239,7 @@
             multiInstanceHelper,
             shellExecutor,
             Optional.of(desktopTasksLimiter),
+            recentTasksController
         )
     }
 
@@ -622,7 +629,7 @@
         controller.moveToDesktop(task)
         val wct = getLatestMoveToDesktopWct()
         assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
-            .isEqualTo(WINDOWING_MODE_FREEFORM)
+                .isEqualTo(WINDOWING_MODE_FREEFORM)
     }
 
     @Test
@@ -643,14 +650,17 @@
     }
 
     @Test
-    fun moveToDesktop_deviceNotSupported_doesNothing() {
-        val task = setUpFullscreenTask()
+    fun moveToDesktop_nonRunningTask_launchesInFreeform() {
+        whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
 
-        // Simulate non compatible device
-        doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+        val task = createTaskInfo(1)
 
-        controller.moveToDesktop(task)
-        verifyWCTNotExecuted()
+        whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
+
+        controller.moveToDesktop(task.taskId)
+        with(getLatestMoveToDesktopWct()){
+            assertLaunchTaskAt(0, task.taskId, WINDOWING_MODE_FREEFORM)
+        }
     }
 
     @Test
@@ -666,6 +676,17 @@
     }
 
     @Test
+    fun moveToDesktop_deviceNotSupported_doesNothing() {
+        val task = setUpFullscreenTask()
+
+        // Simulate non compatible device
+        doReturn(false).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+
+        controller.moveToDesktop(task)
+        verifyWCTNotExecuted()
+    }
+
+    @Test
     fun moveToDesktop_deviceNotSupported_deviceRestrictionsOverridden_taskIsMovedToDesktop() {
         val task = setUpFullscreenTask()
 
@@ -1834,6 +1855,20 @@
     assertThat(op.pendingIntent?.intent?.component).isEqualTo(intent.component)
 }
 
+private fun WindowContainerTransaction.assertLaunchTaskAt(
+    index: Int,
+    taskId: Int,
+    windowingMode: Int
+) {
+    val keyLaunchWindowingMode = "android.activity.windowingMode"
+
+    assertIndexInBounds(index)
+    val op = hierarchyOps[index]
+    assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_LAUNCH_TASK)
+    assertThat(op.launchOptions?.getInt(LAUNCH_KEY_TASK_ID)).isEqualTo(taskId)
+    assertThat(op.launchOptions?.getInt(keyLaunchWindowingMode, WINDOWING_MODE_UNDEFINED))
+            .isEqualTo(windowingMode)
+}
 private fun WindowContainerTransaction?.anyDensityConfigChange(
     token: WindowContainerToken
 ): Boolean {
@@ -1841,3 +1876,7 @@
         change.key == token.asBinder() && ((change.value.configSetMask and CONFIG_DENSITY) != 0)
     } ?: false
 }
+private fun createTaskInfo(id: Int) = RecentTaskInfo().apply {
+    taskId = id
+    token = WindowContainerToken(mock(IWindowContainerToken::class.java))
+}
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 aa2cee7..9c1dc22 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
@@ -26,6 +26,7 @@
 import android.graphics.Rect
 import android.hardware.display.DisplayManager
 import android.hardware.display.VirtualDisplay
+import android.hardware.input.InputManager
 import android.os.Handler
 import android.platform.test.annotations.EnableFlags
 import android.platform.test.annotations.RequiresFlagsEnabled
@@ -42,8 +43,10 @@
 import android.view.InputMonitor
 import android.view.InsetsSource
 import android.view.InsetsState
+import android.view.KeyEvent
 import android.view.SurfaceControl
 import android.view.SurfaceView
+import android.view.View
 import android.view.WindowInsets.Type.navigationBars
 import android.view.WindowInsets.Type.statusBars
 import androidx.test.filters.SmallTest
@@ -51,6 +54,7 @@
 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
 import com.android.dx.mockito.inline.extended.StaticMockitoSession
 import com.android.window.flags.Flags
+import com.android.wm.shell.R
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer
 import com.android.wm.shell.ShellTaskOrganizer
 import com.android.wm.shell.ShellTestCase
@@ -61,6 +65,7 @@
 import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.common.SyncTransactionQueue
 import com.android.wm.shell.desktopmode.DesktopTasksController
+import com.android.wm.shell.freeform.FreeformTaskTransitionStarter
 import com.android.wm.shell.shared.DesktopModeStatus
 import com.android.wm.shell.sysui.KeyguardChangeListener
 import com.android.wm.shell.sysui.ShellCommandHandler
@@ -70,6 +75,7 @@
 import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel.DesktopModeOnInsetsChangedListener
 import java.util.Optional
 import java.util.function.Supplier
+import org.junit.Assert.assertEquals
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -279,6 +285,41 @@
     }
 
     @Test
+    fun testBackEventHasRightDisplayId() {
+        val secondaryDisplay = createVirtualDisplay() ?: return
+        val secondaryDisplayId = secondaryDisplay.display.displayId
+        val task = createTask(
+            displayId = secondaryDisplayId,
+            windowingMode = WINDOWING_MODE_FREEFORM
+        )
+        val windowDecor = setUpMockDecorationForTask(task)
+
+        onTaskOpening(task)
+        val onClickListenerCaptor = argumentCaptor<View.OnClickListener>()
+        verify(windowDecor).setCaptionListeners(
+            onClickListenerCaptor.capture(), any(), any(), any())
+
+        val onClickListener = onClickListenerCaptor.firstValue
+        val view = mock(View::class.java)
+        whenever(view.id).thenReturn(R.id.back_button)
+
+        val inputManager = mock(InputManager::class.java)
+        mContext.addMockSystemService(InputManager::class.java, inputManager)
+
+        val freeformTaskTransitionStarter = mock(FreeformTaskTransitionStarter::class.java)
+        desktopModeWindowDecorViewModel
+                .setFreeformTaskTransitionStarter(freeformTaskTransitionStarter)
+
+        onClickListener.onClick(view)
+
+        val eventCaptor = argumentCaptor<KeyEvent>()
+        verify(inputManager, times(2)).injectInputEvent(eventCaptor.capture(), anyInt())
+
+        assertEquals(secondaryDisplayId, eventCaptor.firstValue.displayId)
+        assertEquals(secondaryDisplayId, eventCaptor.secondValue.displayId)
+    }
+
+    @Test
     fun testCaptionIsNotCreatedWhenKeyguardIsVisible() {
         val task = createTask(windowingMode = WINDOWING_MODE_FULLSCREEN, focused = true)
         val keyguardListenerCaptor = argumentCaptor<KeyguardChangeListener>()
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 3ca9b57..a731e53 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
@@ -228,7 +228,7 @@
     public void updateRelayoutParams_freeformAndTransparentAppearance_allowsInputFallthrough() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
         taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        taskInfo.taskDescription.setSystemBarsAppearance(
+        taskInfo.taskDescription.setTopOpaqueSystemBarsAppearance(
                 APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND);
         final RelayoutParams relayoutParams = new RelayoutParams();
 
@@ -246,7 +246,7 @@
     public void updateRelayoutParams_freeformButOpaqueAppearance_disallowsInputFallthrough() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
         taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
-        taskInfo.taskDescription.setSystemBarsAppearance(0);
+        taskInfo.taskDescription.setTopOpaqueSystemBarsAppearance(0);
         final RelayoutParams relayoutParams = new RelayoutParams();
 
         DesktopModeWindowDecoration.updateRelayoutParams(
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 8742591..5da57c5 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
@@ -33,6 +33,8 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyFloat
+import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Mock
 import org.mockito.Spy
 import org.mockito.kotlin.any
@@ -102,6 +104,14 @@
             .create("Resize veil icon of Task=" + taskInfo.taskId))
             .thenReturn(spyIconSurfaceBuilder)
         doReturn(mockIconSurface).whenever(spyIconSurfaceBuilder).build()
+
+        doReturn(mockTransaction).whenever(mockTransaction).setLayer(any(), anyInt())
+        doReturn(mockTransaction).whenever(mockTransaction).setAlpha(any(), anyFloat())
+        doReturn(mockTransaction).whenever(mockTransaction).show(any())
+        doReturn(mockTransaction).whenever(mockTransaction).hide(any())
+        doReturn(mockTransaction).whenever(mockTransaction)
+                .setPosition(any(), anyFloat(), anyFloat())
+        doReturn(mockTransaction).whenever(mockTransaction).setWindowCrop(any(), anyInt(), anyInt())
     }
 
     @Test
@@ -139,52 +149,48 @@
     @Test
     fun showVeil() {
         val veil = createResizeVeil()
-        val tx = mock<SurfaceControl.Transaction>()
 
-        veil.showVeil(tx, mock(), Rect(0, 0, 100, 100), false /* fadeIn */)
+        veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), false /* fadeIn */)
 
-        verify(tx).show(mockResizeVeilSurface)
-        verify(tx).show(mockBackgroundSurface)
-        verify(tx).show(mockIconSurface)
-        verify(tx).apply()
+        verify(mockTransaction).show(mockResizeVeilSurface)
+        verify(mockTransaction).show(mockBackgroundSurface)
+        verify(mockTransaction).show(mockIconSurface)
+        verify(mockTransaction).apply()
     }
 
     @Test
     fun showVeil_displayUnavailable_doesNotShow() {
         val veil = createResizeVeil(withDisplayAvailable = false)
-        val tx = mock<SurfaceControl.Transaction>()
 
-        veil.showVeil(tx, mock(), Rect(0, 0, 100, 100), false /* fadeIn */)
+        veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), false /* fadeIn */)
 
-        verify(tx, never()).show(mockResizeVeilSurface)
-        verify(tx, never()).show(mockBackgroundSurface)
-        verify(tx, never()).show(mockIconSurface)
-        verify(tx).apply()
+        verify(mockTransaction, never()).show(mockResizeVeilSurface)
+        verify(mockTransaction, never()).show(mockBackgroundSurface)
+        verify(mockTransaction, never()).show(mockIconSurface)
+        verify(mockTransaction).apply()
     }
 
     @Test
     fun showVeil_alreadyVisible_doesNotShowAgain() {
         val veil = createResizeVeil()
-        val tx = mock<SurfaceControl.Transaction>()
 
-        veil.showVeil(tx, mock(), Rect(0, 0, 100, 100), false /* fadeIn */)
-        veil.showVeil(tx, mock(), Rect(0, 0, 100, 100), false /* fadeIn */)
+        veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), false /* fadeIn */)
+        veil.showVeil(mockTransaction, mock(), Rect(0, 0, 100, 100), false /* fadeIn */)
 
-        verify(tx, times(1)).show(mockResizeVeilSurface)
-        verify(tx, times(1)).show(mockBackgroundSurface)
-        verify(tx, times(1)).show(mockIconSurface)
-        verify(tx, times(2)).apply()
+        verify(mockTransaction, times(1)).show(mockResizeVeilSurface)
+        verify(mockTransaction, times(1)).show(mockBackgroundSurface)
+        verify(mockTransaction, times(1)).show(mockIconSurface)
+        verify(mockTransaction, times(2)).apply()
     }
 
     @Test
     fun showVeil_reparentsVeilToNewParent() {
         val veil = createResizeVeil(parent = mock())
-        val tx = mock<SurfaceControl.Transaction>()
 
         val newParent = mock<SurfaceControl>()
-        veil.showVeil(tx, newParent, Rect(0, 0, 100, 100), false /* fadeIn */)
+        veil.showVeil(mockTransaction, newParent, Rect(0, 0, 100, 100), false /* fadeIn */)
 
-        verify(tx).reparent(mockResizeVeilSurface, newParent)
+        verify(mockTransaction).reparent(mockResizeVeilSurface, newParent)
     }
 
     @Test
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 5ce990f..7b7ccf5 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -48,7 +48,6 @@
         "libgui",
         "libui",
         "libinput",
-        "libnativewindow",
     ],
 
     header_libs: [
diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp
index f1ee325..eecc741 100644
--- a/libs/input/MouseCursorController.cpp
+++ b/libs/input/MouseCursorController.cpp
@@ -165,6 +165,15 @@
     }
 }
 
+void MouseCursorController::setSkipScreenshot(bool skip) {
+    std::scoped_lock lock(mLock);
+    if (mLocked.skipScreenshot == skip) {
+        return;
+    }
+    mLocked.skipScreenshot = skip;
+    updatePointerLocked();
+}
+
 void MouseCursorController::reloadPointerResources(bool getAdditionalMouseResources) {
     std::scoped_lock lock(mLock);
 
@@ -352,6 +361,7 @@
     mLocked.pointerSprite->setLayer(Sprite::BASE_LAYER_POINTER);
     mLocked.pointerSprite->setPosition(mLocked.pointerX, mLocked.pointerY);
     mLocked.pointerSprite->setDisplayId(mLocked.viewport.displayId);
+    mLocked.pointerSprite->setSkipScreenshot(mLocked.skipScreenshot);
 
     if (mLocked.pointerAlpha > 0) {
         mLocked.pointerSprite->setAlpha(mLocked.pointerAlpha);
diff --git a/libs/input/MouseCursorController.h b/libs/input/MouseCursorController.h
index dc7e8ca..78f6413 100644
--- a/libs/input/MouseCursorController.h
+++ b/libs/input/MouseCursorController.h
@@ -53,6 +53,9 @@
     void setDisplayViewport(const DisplayViewport& viewport, bool getAdditionalMouseResources);
     void setStylusHoverMode(bool stylusHoverMode);
 
+    // Set/Unset flag to hide the mouse cursor on the mirrored display
+    void setSkipScreenshot(bool skip);
+
     void updatePointerIcon(PointerIconStyle iconId);
     void setCustomPointerIcon(const SpriteIcon& icon);
     void reloadPointerResources(bool getAdditionalMouseResources);
@@ -94,6 +97,7 @@
         PointerIconStyle requestedPointerType;
         PointerIconStyle resolvedPointerType;
 
+        bool skipScreenshot{false};
         bool animating{false};
 
     } mLocked GUARDED_BY(mLock);
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index cca1b07..11b27a2 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -286,13 +286,16 @@
     mCursorController.setCustomPointerIcon(icon);
 }
 
-void PointerController::setSkipScreenshot(ui::LogicalDisplayId displayId, bool skip) {
+void PointerController::setSkipScreenshotFlagForDisplay(ui::LogicalDisplayId displayId) {
     std::scoped_lock lock(getLock());
-    if (skip) {
-        mLocked.displaysToSkipScreenshot.insert(displayId);
-    } else {
-        mLocked.displaysToSkipScreenshot.erase(displayId);
-    }
+    mLocked.displaysToSkipScreenshot.insert(displayId);
+    mCursorController.setSkipScreenshot(true);
+}
+
+void PointerController::clearSkipScreenshotFlags() {
+    std::scoped_lock lock(getLock());
+    mLocked.displaysToSkipScreenshot.clear();
+    mCursorController.setSkipScreenshot(false);
 }
 
 void PointerController::doInactivityTimeout() {
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index c6430f7..4d1e1d7 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -66,7 +66,8 @@
     void clearSpots() override;
     void updatePointerIcon(PointerIconStyle iconId) override;
     void setCustomPointerIcon(const SpriteIcon& icon) override;
-    void setSkipScreenshot(ui::LogicalDisplayId displayId, bool skip) override;
+    void setSkipScreenshotFlagForDisplay(ui::LogicalDisplayId displayId) override;
+    void clearSkipScreenshotFlags() override;
 
     virtual void setInactivityTimeout(InactivityTimeout inactivityTimeout);
     void doInactivityTimeout();
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index 2dcb1f1..5b00fca 100644
--- a/libs/input/tests/PointerController_test.cpp
+++ b/libs/input/tests/PointerController_test.cpp
@@ -162,6 +162,16 @@
 };
 
 class PointerControllerTest : public Test {
+private:
+    void loopThread();
+
+    std::atomic<bool> mRunning = true;
+    class MyLooper : public Looper {
+    public:
+        MyLooper() : Looper(false) {}
+        ~MyLooper() = default;
+    };
+
 protected:
     PointerControllerTest();
     ~PointerControllerTest();
@@ -173,22 +183,16 @@
     std::unique_ptr<MockSpriteController> mSpriteController;
     std::shared_ptr<PointerController> mPointerController;
     sp<android::gui::WindowInfosListener> mRegisteredListener;
+    sp<MyLooper> mLooper;
 
 private:
-    void loopThread();
-
-    std::atomic<bool> mRunning = true;
-    class MyLooper : public Looper {
-    public:
-        MyLooper() : Looper(false) {}
-        ~MyLooper() = default;
-    };
-    sp<MyLooper> mLooper;
     std::thread mThread;
 };
 
-PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<MockSprite>),
-        mLooper(new MyLooper), mThread(&PointerControllerTest::loopThread, this) {
+PointerControllerTest::PointerControllerTest()
+      : mPointerSprite(new NiceMock<MockSprite>),
+        mLooper(new MyLooper),
+        mThread(&PointerControllerTest::loopThread, this) {
     mSpriteController.reset(new NiceMock<MockSpriteController>(mLooper));
     mPolicy = new MockPointerControllerPolicyInterface();
 
@@ -339,7 +343,7 @@
     testing::Mock::VerifyAndClearExpectations(testSpotSprite.get());
 
     // Marking the display to skip screenshot should update sprite as well
-    mPointerController->setSkipScreenshot(ui::LogicalDisplayId::DEFAULT, true);
+    mPointerController->setSkipScreenshotFlagForDisplay(ui::LogicalDisplayId::DEFAULT);
     EXPECT_CALL(*testSpotSprite, setSkipScreenshot).With(testing::Args<0>(true));
 
     // Update spots to sync state with sprite
@@ -348,13 +352,53 @@
     testing::Mock::VerifyAndClearExpectations(testSpotSprite.get());
 
     // Reset flag and verify again
-    mPointerController->setSkipScreenshot(ui::LogicalDisplayId::DEFAULT, false);
+    mPointerController->clearSkipScreenshotFlags();
     EXPECT_CALL(*testSpotSprite, setSkipScreenshot).With(testing::Args<0>(false));
     mPointerController->setSpots(&testSpotCoords, testIdToIndex.cbegin(), testIdBits,
                                  ui::LogicalDisplayId::DEFAULT);
     testing::Mock::VerifyAndClearExpectations(testSpotSprite.get());
 }
 
+class PointerControllerSkipScreenshotFlagTest
+      : public PointerControllerTest,
+        public testing::WithParamInterface<PointerControllerInterface::ControllerType> {};
+
+TEST_P(PointerControllerSkipScreenshotFlagTest, updatesSkipScreenshotFlag) {
+    sp<MockSprite> testPointerSprite(new NiceMock<MockSprite>);
+    EXPECT_CALL(*mSpriteController, createSprite).WillOnce(Return(testPointerSprite));
+
+    // Create a pointer controller
+    mPointerController =
+            PointerController::create(mPolicy, mLooper, *mSpriteController, GetParam());
+    ensureDisplayViewportIsSet(ui::LogicalDisplayId::DEFAULT);
+
+    // By default skip screenshot flag is not set for the sprite
+    EXPECT_CALL(*testPointerSprite, setSkipScreenshot).With(testing::Args<0>(false));
+
+    // Update pointer to sync state with sprite
+    mPointerController->setPosition(100, 100);
+    testing::Mock::VerifyAndClearExpectations(testPointerSprite.get());
+
+    // Marking the controller to skip screenshot should update pointer sprite
+    mPointerController->setSkipScreenshotFlagForDisplay(ui::LogicalDisplayId::DEFAULT);
+    EXPECT_CALL(*testPointerSprite, setSkipScreenshot).With(testing::Args<0>(true));
+
+    // Update pointer to sync state with sprite
+    mPointerController->move(10, 10);
+    testing::Mock::VerifyAndClearExpectations(testPointerSprite.get());
+
+    // Reset flag and verify again
+    mPointerController->clearSkipScreenshotFlags();
+    EXPECT_CALL(*testPointerSprite, setSkipScreenshot).With(testing::Args<0>(false));
+    mPointerController->move(10, 10);
+    testing::Mock::VerifyAndClearExpectations(testPointerSprite.get());
+}
+
+INSTANTIATE_TEST_SUITE_P(PointerControllerSkipScreenshotFlagTest,
+                         PointerControllerSkipScreenshotFlagTest,
+                         testing::Values(PointerControllerInterface::ControllerType::MOUSE,
+                                         PointerControllerInterface::ControllerType::STYLUS));
+
 class PointerControllerWindowInfoListenerTest : public Test {};
 
 TEST_F(PointerControllerWindowInfoListenerTest,
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 293c561..d148afd 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1764,6 +1764,10 @@
     public static native int getForceUse(int usage);
     /** @hide */
     @UnsupportedAppUsage
+    public static native int setDeviceAbsoluteVolumeEnabled(int nativeDeviceType,
+            @NonNull String address, boolean enabled, int streamToDriveAbs);
+    /** @hide */
+    @UnsupportedAppUsage
     public static native int initStreamVolume(int stream, int indexMin, int indexMax);
     @UnsupportedAppUsage
     private static native int setStreamVolumeIndex(int stream, int index, int device);
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 679e8a1..5672cd5 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -681,8 +681,19 @@
     /**
      * Registers a callback to discover routes and to receive events when they change.
      *
+     * <p>Clients can register multiple callbacks, as long as the {@link RouteCallback} instances
+     * are different. Each callback can provide a unique {@link RouteDiscoveryPreference preference}
+     * and will only receive updates related to that set preference.
+     *
      * <p>If the specified callback is already registered, its registration will be updated for the
      * given {@link Executor executor} and {@link RouteDiscoveryPreference discovery preference}.
+     *
+     * <p>{@link #getInstance(Context) Local routers} must register a route callback to register in
+     * the system and start receiving updates. Otherwise, all operations will be no-ops.
+     *
+     * <p>Any discovery preference passed by a {@link #getInstance(Context, String) proxy router}
+     * will be ignored and will receive route updates based on the preference set by its matching
+     * local router.
      */
     public void registerRouteCallback(
             @NonNull @CallbackExecutor Executor executor,
diff --git a/media/java/android/media/projection/IMediaProjection.aidl b/media/java/android/media/projection/IMediaProjection.aidl
index 2fb0af5..7a1cf92 100644
--- a/media/java/android/media/projection/IMediaProjection.aidl
+++ b/media/java/android/media/projection/IMediaProjection.aidl
@@ -39,8 +39,8 @@
     void unregisterCallback(IMediaProjectionCallback callback);
 
     /**
-     * Returns the {@link LaunchCookie} identifying the task to record, or {@code null} if
-     * there is none.
+     * Returns the {@link LaunchCookie} identifying the task to record. Will always be set
+     * regardless of starting a new task or recent task
      */
     @EnforcePermission("MANAGE_MEDIA_PROJECTION")
     @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
@@ -48,8 +48,16 @@
     LaunchCookie getLaunchCookie();
 
     /**
-     * Updates the {@link LaunchCookie} identifying the task to record, or {@code null} if
-     * there is none.
+     * Returns the taskId identifying the task to record. Will only be set in the case of
+     * launching a recent task, otherwise set to -1.
+     */
+    @EnforcePermission("MANAGE_MEDIA_PROJECTION")
+    @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+            + ".permission.MANAGE_MEDIA_PROJECTION)")
+    int getTaskId();
+
+    /**
+     * Updates the {@link LaunchCookie} identifying the task to record.
      */
     @EnforcePermission("MANAGE_MEDIA_PROJECTION")
     @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
@@ -57,6 +65,15 @@
     void setLaunchCookie(in LaunchCookie launchCookie);
 
     /**
+     * Updates the taskId identifying the task to record.
+     */
+    @EnforcePermission("MANAGE_MEDIA_PROJECTION")
+    @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest"
+            + ".permission.MANAGE_MEDIA_PROJECTION)")
+    void setTaskId(in int taskId);
+
+
+    /**
      * Returns {@code true} if this token is still valid. A token is valid as long as the token
      * hasn't timed out before it was used, and the token is only used once.
      *
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 70462ef..442ccdc 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -141,10 +141,9 @@
         }
         try {
             return mSessionBinder.sendMediaButton(mContext.getPackageName(), keyEvent);
-        } catch (RemoteException e) {
-            // System is dead. =(
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
-        return false;
     }
 
     /**
@@ -155,9 +154,8 @@
     public @Nullable PlaybackState getPlaybackState() {
         try {
             return mSessionBinder.getPlaybackState();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling getPlaybackState.", e);
-            return null;
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -169,9 +167,8 @@
     public @Nullable MediaMetadata getMetadata() {
         try {
             return mSessionBinder.getMetadata();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling getMetadata.", e);
-            return null;
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -185,10 +182,9 @@
         try {
             ParceledListSlice list = mSessionBinder.getQueue();
             return list == null ? null : list.getList();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling getQueue.", e);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
-        return null;
     }
 
     /**
@@ -197,10 +193,9 @@
     public @Nullable CharSequence getQueueTitle() {
         try {
             return mSessionBinder.getQueueTitle();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling getQueueTitle", e);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
-        return null;
     }
 
     /**
@@ -209,10 +204,9 @@
     public @Nullable Bundle getExtras() {
         try {
             return mSessionBinder.getExtras();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling getExtras", e);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
-        return null;
     }
 
     /**
@@ -232,9 +226,8 @@
     public int getRatingType() {
         try {
             return mSessionBinder.getRatingType();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling getRatingType.", e);
-            return Rating.RATING_NONE;
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -246,10 +239,9 @@
     public long getFlags() {
         try {
             return mSessionBinder.getFlags();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling getFlags.", e);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
-        return 0;
     }
 
     /** Returns the current playback info for this session. */
@@ -271,10 +263,9 @@
     public @Nullable PendingIntent getSessionActivity() {
         try {
             return mSessionBinder.getLaunchPendingIntent();
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling getPendingIntent.", e);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
-        return null;
     }
 
     /**
@@ -304,8 +295,8 @@
             //       AppOpsManager usages.
             mSessionBinder.setVolumeTo(mContext.getPackageName(), mContext.getOpPackageName(),
                     value, flags);
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling setVolumeTo.", e);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -329,8 +320,8 @@
             //       AppOpsManager usages.
             mSessionBinder.adjustVolume(mContext.getPackageName(), mContext.getOpPackageName(),
                     direction, flags);
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -395,8 +386,8 @@
         }
         try {
             mSessionBinder.sendCommand(mContext.getPackageName(), command, args, cb);
-        } catch (RemoteException e) {
-            Log.d(TAG, "Dead object in sendCommand.", e);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
     }
 
@@ -409,8 +400,8 @@
         if (mPackageName == null) {
             try {
                 mPackageName = mSessionBinder.getPackageName();
-            } catch (RemoteException e) {
-                Log.d(TAG, "Dead object in getPackageName.", e);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
         }
         return mPackageName;
@@ -430,8 +421,8 @@
         // Get info from the connected session.
         try {
             mSessionInfo = mSessionBinder.getSessionInfo();
-        } catch (RemoteException e) {
-            Log.d(TAG, "Dead object in getSessionInfo.", e);
+        } catch (RemoteException ex) {
+            throw ex.rethrowFromSystemServer();
         }
 
         if (mSessionInfo == null) {
@@ -454,8 +445,8 @@
         if (mTag == null) {
             try {
                 mTag = mSessionBinder.getTag();
-            } catch (RemoteException e) {
-                Log.d(TAG, "Dead object in getTag.", e);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
         }
         return mTag;
@@ -485,8 +476,8 @@
             try {
                 mSessionBinder.registerCallback(mContext.getPackageName(), mCbStub);
                 mCbRegistered = true;
-            } catch (RemoteException e) {
-                Log.e(TAG, "Dead object in registerCallback", e);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
         }
     }
@@ -504,8 +495,8 @@
         if (mCbRegistered && mCallbacks.size() == 0) {
             try {
                 mSessionBinder.unregisterCallback(mCbStub);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Dead object in removeCallbackLocked");
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
             mCbRegistered = false;
         }
@@ -641,8 +632,8 @@
         public void prepare() {
             try {
                 mSessionBinder.prepare(mContext.getPackageName());
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling prepare.", e);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
         }
 
@@ -665,8 +656,8 @@
             }
             try {
                 mSessionBinder.prepareFromMediaId(mContext.getPackageName(), mediaId, extras);
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling prepare(" + mediaId + ").", e);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
         }
 
@@ -691,8 +682,8 @@
             }
             try {
                 mSessionBinder.prepareFromSearch(mContext.getPackageName(), query, extras);
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling prepare(" + query + ").", e);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
         }
 
@@ -715,8 +706,8 @@
             }
             try {
                 mSessionBinder.prepareFromUri(mContext.getPackageName(), uri, extras);
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling prepare(" + uri + ").", e);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
         }
 
@@ -726,8 +717,8 @@
         public void play() {
             try {
                 mSessionBinder.play(mContext.getPackageName());
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling play.", e);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
         }
 
@@ -745,8 +736,8 @@
             }
             try {
                 mSessionBinder.playFromMediaId(mContext.getPackageName(), mediaId, extras);
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling play(" + mediaId + ").", e);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
         }
 
@@ -767,8 +758,8 @@
             }
             try {
                 mSessionBinder.playFromSearch(mContext.getPackageName(), query, extras);
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling play(" + query + ").", e);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
         }
 
@@ -786,8 +777,8 @@
             }
             try {
                 mSessionBinder.playFromUri(mContext.getPackageName(), uri, extras);
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling play(" + uri + ").", e);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
         }
 
@@ -798,8 +789,8 @@
         public void skipToQueueItem(long id) {
             try {
                 mSessionBinder.skipToQueueItem(mContext.getPackageName(), id);
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
         }
 
@@ -810,8 +801,8 @@
         public void pause() {
             try {
                 mSessionBinder.pause(mContext.getPackageName());
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling pause.", e);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
         }
 
@@ -822,8 +813,8 @@
         public void stop() {
             try {
                 mSessionBinder.stop(mContext.getPackageName());
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling stop.", e);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
         }
 
@@ -835,8 +826,8 @@
         public void seekTo(long pos) {
             try {
                 mSessionBinder.seekTo(mContext.getPackageName(), pos);
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling seekTo.", e);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
         }
 
@@ -847,8 +838,8 @@
         public void fastForward() {
             try {
                 mSessionBinder.fastForward(mContext.getPackageName());
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling fastForward.", e);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
         }
 
@@ -858,8 +849,8 @@
         public void skipToNext() {
             try {
                 mSessionBinder.next(mContext.getPackageName());
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling next.", e);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
         }
 
@@ -870,8 +861,8 @@
         public void rewind() {
             try {
                 mSessionBinder.rewind(mContext.getPackageName());
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling rewind.", e);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
         }
 
@@ -881,8 +872,8 @@
         public void skipToPrevious() {
             try {
                 mSessionBinder.previous(mContext.getPackageName());
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling previous.", e);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
         }
 
@@ -896,8 +887,8 @@
         public void setRating(Rating rating) {
             try {
                 mSessionBinder.rate(mContext.getPackageName(), rating);
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling rate.", e);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
         }
 
@@ -914,8 +905,8 @@
             }
             try {
                 mSessionBinder.setPlaybackSpeed(mContext.getPackageName(), speed);
-            } catch (RemoteException e) {
-                Log.wtf(TAG, "Error calling setPlaybackSpeed.", e);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
         }
 
@@ -949,8 +940,8 @@
             }
             try {
                 mSessionBinder.sendCustomAction(mContext.getPackageName(), action, args);
-            } catch (RemoteException e) {
-                Log.d(TAG, "Dead object in sendCustomAction.", e);
+            } catch (RemoteException ex) {
+                throw ex.rethrowFromSystemServer();
             }
         }
     }
diff --git a/media/tests/MediaRouter/Android.bp b/media/tests/MediaRouter/Android.bp
index 61b18c8..d21cb93 100644
--- a/media/tests/MediaRouter/Android.bp
+++ b/media/tests/MediaRouter/Android.bp
@@ -9,6 +9,7 @@
 
 android_test {
     name: "mediaroutertest",
+    team: "trendy_team_android_media_solutions",
 
     srcs: ["**/*.java"],
 
diff --git a/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java b/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java
index 0df36af..6860c0b 100644
--- a/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java
+++ b/media/tests/projection/src/android/media/projection/FakeIMediaProjection.java
@@ -28,6 +28,7 @@
  * outside the test it is implemented by the system server.
  */
 public final class FakeIMediaProjection extends IMediaProjection.Stub {
+    int mTaskId = -1;
     boolean mIsStarted = false;
     LaunchCookie mLaunchCookie = null;
     IMediaProjectionCallback mIMediaProjectionCallback = null;
@@ -87,6 +88,13 @@
 
     @Override
     @EnforcePermission(MANAGE_MEDIA_PROJECTION)
+    public int getTaskId() throws RemoteException {
+        getTaskId_enforcePermission();
+        return mTaskId;
+    }
+
+    @Override
+    @EnforcePermission(MANAGE_MEDIA_PROJECTION)
     public void setLaunchCookie(LaunchCookie launchCookie) throws RemoteException {
         setLaunchCookie_enforcePermission();
         mLaunchCookie = launchCookie;
@@ -94,6 +102,13 @@
 
     @Override
     @EnforcePermission(MANAGE_MEDIA_PROJECTION)
+    public void setTaskId(int taskId) throws RemoteException {
+        setTaskId_enforcePermission();
+        mTaskId = taskId;
+    }
+
+    @Override
+    @EnforcePermission(MANAGE_MEDIA_PROJECTION)
     public boolean isValid() throws RemoteException {
         isValid_enforcePermission();
         return true;
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index a33e225..055ccbc 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -27,6 +27,7 @@
     field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String ACTION_REQUIRE_UNLOCK_FOR_NFC = "android.nfc.action.REQUIRE_UNLOCK_FOR_NFC";
     field @FlaggedApi("android.nfc.enable_nfc_mainline") @RequiresPermission(android.Manifest.permission.SHOW_CUSTOMIZED_RESOLVER) public static final String ACTION_SHOW_NFC_RESOLVER = "android.nfc.action.SHOW_NFC_RESOLVER";
     field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String EXTRA_RESOLVE_INFOS = "android.nfc.extra.RESOLVE_INFOS";
+    field @FlaggedApi("android.nfc.nfc_set_default_disc_tech") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static final int FLAG_SET_DEFAULT_TECH = 1073741824; // 0x40000000
     field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int MESSAGE_TYPE_COMMAND = 1; // 0x1
     field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int SEND_VENDOR_NCI_STATUS_FAILED = 3; // 0x3
     field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int SEND_VENDOR_NCI_STATUS_MESSAGE_CORRUPTED = 2; // 0x2
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index 698df28..1dfc81e 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -340,7 +340,8 @@
     public static final int FLAG_READER_NFC_BARCODE = 0x10;
 
     /** @hide */
-    @IntDef(flag = true, prefix = {"FLAG_READER_"}, value = {
+    @IntDef(flag = true, value = {
+        FLAG_SET_DEFAULT_TECH,
         FLAG_READER_KEEP,
         FLAG_READER_DISABLE,
         FLAG_READER_NFC_A,
@@ -438,7 +439,8 @@
     public static final int FLAG_USE_ALL_TECH = 0xff;
 
     /** @hide */
-    @IntDef(flag = true, prefix = {"FLAG_LISTEN_"}, value = {
+    @IntDef(flag = true, value = {
+        FLAG_SET_DEFAULT_TECH,
         FLAG_LISTEN_KEEP,
         FLAG_LISTEN_DISABLE,
         FLAG_LISTEN_NFC_PASSIVE_A,
@@ -449,6 +451,18 @@
     public @interface ListenTechnology {}
 
     /**
+     * Flag used in {@link #setDiscoveryTechnology(Activity, int, int)}.
+     * <p>
+     * Setting this flag changes the default listen or poll tech.
+     * Only available to privileged apps.
+     * @hide
+     */
+    @SystemApi
+    @FlaggedApi(Flags.FLAG_NFC_SET_DEFAULT_DISC_TECH)
+    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    public static final int FLAG_SET_DEFAULT_TECH = 0x40000000;
+
+    /**
      * @hide
      * @removed
      */
@@ -1874,14 +1888,6 @@
     public void setDiscoveryTechnology(@NonNull Activity activity,
             @PollTechnology int pollTechnology, @ListenTechnology int listenTechnology) {
 
-        // A special treatment of the _KEEP flags
-        if ((listenTechnology & FLAG_LISTEN_KEEP) != 0) {
-            listenTechnology = -1;
-        }
-        if ((pollTechnology & FLAG_READER_KEEP) != 0) {
-            pollTechnology = -1;
-        }
-
         if (listenTechnology == FLAG_LISTEN_DISABLE) {
             synchronized (sLock) {
                 if (!sHasNfcFeature) {
@@ -1901,7 +1907,25 @@
                 }
             }
         }
-        mNfcActivityManager.setDiscoveryTech(activity, pollTechnology, listenTechnology);
+    /*
+     * Privileged FLAG to set technology mask for all data processed by NFC controller
+     * Note: Use with caution! The app is responsible for ensuring that the discovery
+     * technology mask is returned to default.
+     * Note: FLAG_USE_ALL_TECH used with _KEEP flags will reset the technolody to android default
+     */
+        if (Flags.nfcSetDefaultDiscTech()
+                && ((pollTechnology & FLAG_SET_DEFAULT_TECH) == FLAG_SET_DEFAULT_TECH
+                || (listenTechnology & FLAG_SET_DEFAULT_TECH) == FLAG_SET_DEFAULT_TECH)) {
+            Binder token = new Binder();
+            try {
+                NfcAdapter.sService.updateDiscoveryTechnology(token,
+                        pollTechnology, listenTechnology);
+            } catch (RemoteException e) {
+                attemptDeadServiceRecovery(e);
+            }
+        } else {
+            mNfcActivityManager.setDiscoveryTech(activity, pollTechnology, listenTechnology);
+        }
     }
 
     /**
diff --git a/nfc/java/android/nfc/cardemulation/PollingFrame.java b/nfc/java/android/nfc/cardemulation/PollingFrame.java
index 4c76fb0..5dcc84c 100644
--- a/nfc/java/android/nfc/cardemulation/PollingFrame.java
+++ b/nfc/java/android/nfc/cardemulation/PollingFrame.java
@@ -16,7 +16,6 @@
 
 package android.nfc.cardemulation;
 
-import android.annotation.DurationMillisLong;
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -33,13 +32,13 @@
 
 /**
  * Polling Frames represent data about individual frames of an NFC polling loop. These frames will
- * be deliverd to subclasses of {@link HostApduService} that have registered filters with
- * {@link CardEmulation#registerPollingLoopFilterForService(ComponentName, String)} that match a
- * given frame in a loop and will be delivered through calls to
+ * be delivered to subclasses of {@link HostApduService} that have registered filters with
+ * {@link CardEmulation#registerPollingLoopFilterForService(ComponentName, String, boolean)} that
+ * match a given frame in a loop and will be delivered through calls to
  * {@link HostApduService#processPollingFrames(List)}.
  */
 @FlaggedApi(android.nfc.Flags.FLAG_NFC_READ_POLLING_LOOP)
-public final class PollingFrame implements Parcelable{
+public final class PollingFrame implements Parcelable {
 
     /**
      * @hide
@@ -146,7 +145,6 @@
     private final int mType;
     private final byte[] mData;
     private final int mGain;
-    @DurationMillisLong
     private final long mTimestamp;
     private boolean mTriggeredAutoTransact;
 
@@ -179,18 +177,18 @@
      * @param type the type of the frame
      * @param data a byte array of the data contained in the frame
      * @param gain the vendor-specific gain of the field
-     * @param timestampMillis the timestamp in millisecones
+     * @param timestampMicros the timestamp in microseconds
      * @param triggeredAutoTransact whether or not this frame triggered the device to start a
      * transaction automatically
      *
      * @hide
      */
     public PollingFrame(@PollingFrameType int type, @Nullable byte[] data,
-            int gain, @DurationMillisLong long timestampMillis, boolean triggeredAutoTransact) {
+            int gain, long timestampMicros, boolean triggeredAutoTransact) {
         mType = type;
         mData = data == null ? new byte[0] : data;
         mGain = gain;
-        mTimestamp = timestampMillis;
+        mTimestamp = timestampMicros;
         mTriggeredAutoTransact = triggeredAutoTransact;
     }
 
@@ -198,11 +196,11 @@
      * Returns the type of frame for this polling loop frame.
      * The possible return values are:
      * <ul>
-     *   <li>{@link POLLING_LOOP_TYPE_ON}</li>
-     *   <li>{@link POLLING_LOOP_TYPE_OFF}</li>
-     *   <li>{@link POLLING_LOOP_TYPE_A}</li>
-     *   <li>{@link POLLING_LOOP_TYPE_B}</li>
-     *   <li>{@link POLLING_LOOP_TYPE_F}</li>
+     *   <li>{@link #POLLING_LOOP_TYPE_ON}</li>
+     *   <li>{@link #POLLING_LOOP_TYPE_OFF}</li>
+     *   <li>{@link #POLLING_LOOP_TYPE_A}</li>
+     *   <li>{@link #POLLING_LOOP_TYPE_B}</li>
+     *   <li>{@link #POLLING_LOOP_TYPE_F}</li>
      * </ul>
      */
     public @PollingFrameType int getType() {
@@ -226,12 +224,12 @@
     }
 
     /**
-     * Returns the timestamp of when the polling loop frame was observed in milliseconds. These
-     * timestamps are relative and not absolute and should only be used for comparing the timing of
-     * frames relative to each other.
-     * @return the timestamp in milliseconds
+     * Returns the timestamp of when the polling loop frame was observed, in microseconds. These
+     * timestamps are relative and should only be used for comparing the timing of frames relative
+     * to each other.
+     * @return the timestamp in microseconds
      */
-    public @DurationMillisLong long getTimestamp() {
+    public long getTimestamp() {
         return mTimestamp;
     }
 
diff --git a/nfc/java/android/nfc/flags.aconfig b/nfc/java/android/nfc/flags.aconfig
index cb2a48c..b242a76 100644
--- a/nfc/java/android/nfc/flags.aconfig
+++ b/nfc/java/android/nfc/flags.aconfig
@@ -101,3 +101,12 @@
     description: "Enable nfc state change API"
     bug: "319934052"
 }
+
+flag {
+    name: "nfc_set_default_disc_tech"
+    is_exported: true
+    namespace: "nfc"
+    description: "Flag for NFC set default disc tech API"
+    bug: "321311407"
+}
+
diff --git a/packages/CredentialManager/res/values-hr/strings.xml b/packages/CredentialManager/res/values-hr/strings.xml
index 39abbb986..9ddc7ab 100644
--- a/packages/CredentialManager/res/values-hr/strings.xml
+++ b/packages/CredentialManager/res/values-hr/strings.xml
@@ -75,7 +75,7 @@
     <string name="get_dialog_title_unlock_options_for" msgid="7096423827682163270">"Otključajte opcije za prijavu za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="get_dialog_title_choose_passkey_for" msgid="9175997688078538490">"Odaberite spremljeni pristupni ključ za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="get_dialog_title_choose_password_for" msgid="1724435823820819221">"Odaberite spremljenu zaporku za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
-    <string name="get_dialog_title_choose_saved_sign_in_for" msgid="2420298653461652728">"Odaberite spremljene podatke za prijavu za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="get_dialog_title_choose_saved_sign_in_for" msgid="2420298653461652728">"Odaberite podatke za prijavu za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g> koje želite spremiti"</string>
     <string name="get_dialog_title_choose_sign_in_for" msgid="645728947702442421">"Odaberite račun za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="get_dialog_title_choose_option_for" msgid="4976380044745029107">"Želite li odabrati opciju za <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
     <string name="get_dialog_title_use_info_on" msgid="8863708099535435146">"Želite li koristiti te podatke u aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
index 9c8ec3b..3022fa0 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/ktx/CredentialKtx.kt
@@ -27,6 +27,7 @@
 import android.credentials.selection.Entry
 import android.credentials.selection.GetCredentialProviderData
 import android.graphics.drawable.Drawable
+import android.os.Bundle
 import android.text.TextUtils
 import android.util.Log
 import androidx.activity.result.IntentSenderRequest
@@ -227,26 +228,31 @@
  * and get flows utilize slice params; includes the final '.' before the name of the type (e.g.
  * androidx.credentials.provider.credentialEntry.SLICE_HINT_ALLOWED_AUTHENTICATORS must have
  * 'hintPrefix' up to "androidx.credentials.provider.credentialEntry.")
- * // TODO(b/326243754) : Presently, due to dependencies, the opId bit is parsed but is never
- * // expected to be used. When it is added, it should be lightly validated.
  */
 fun retrieveEntryBiometricRequest(
     entry: Entry,
-    hintPrefix: String,
+    hintPrefix: String
 ): BiometricRequestInfo? {
-    // TODO(b/326243754) : When available, use the official jetpack structured type
-    val allowedAuthenticators: Int? = entry.slice.items.firstOrNull {
-        it.hasHint(hintPrefix + "SLICE_HINT_ALLOWED_AUTHENTICATORS")
-    }?.int
+    // TODO(b/326243754) : When available, use the official jetpack structured typLo
+    val biometricPromptDataBundleKey = "SLICE_HINT_BIOMETRIC_PROMPT_DATA"
+    val biometricPromptDataBundle: Bundle = entry.slice.items.firstOrNull {
+        it.hasHint(hintPrefix + biometricPromptDataBundleKey)
+    }?.bundle ?: return null
+
+    val allowedAuthConstantKey = "androidx.credentials.provider.BUNDLE_HINT_ALLOWED_AUTHENTICATORS"
+    val cryptoOpIdKey = "androidx.credentials.provider.BUNDLE_HINT_CRYPTO_OP_ID"
+
+    if (!biometricPromptDataBundle.containsKey(allowedAuthConstantKey)) {
+        return null
+    }
+
+    val allowedAuthenticators: Int = biometricPromptDataBundle.getInt(allowedAuthConstantKey)
 
     // This is optional and does not affect validating the biometric flow in any case
-    val opId: Int? = entry.slice.items.firstOrNull {
-        it.hasHint(hintPrefix + "SLICE_HINT_CRYPTO_OP_ID")
-    }?.int
-    if (allowedAuthenticators != null) {
-        return BiometricRequestInfo(opId = opId, allowedAuthenticators = allowedAuthenticators)
-    }
-    return null
+    val opId: Long? = if (biometricPromptDataBundle.containsKey(cryptoOpIdKey))
+        biometricPromptDataBundle.getLong(cryptoOpIdKey) else null
+
+    return BiometricRequestInfo(opId = opId, allowedAuthenticators = allowedAuthenticators)
 }
 
 val Slice.credentialEntry: CredentialEntry?
diff --git a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/BiometricRequestInfo.kt b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/BiometricRequestInfo.kt
index 486cfe7..fe4bead 100644
--- a/packages/CredentialManager/shared/src/com/android/credentialmanager/model/BiometricRequestInfo.kt
+++ b/packages/CredentialManager/shared/src/com/android/credentialmanager/model/BiometricRequestInfo.kt
@@ -23,6 +23,6 @@
  * null.
  */
 data class BiometricRequestInfo(
-    val opId: Int? = null,
+    val opId: Long? = null,
     val allowedAuthenticators: Int
 )
\ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
index 7bc25ed..894d5ef 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt
@@ -135,16 +135,18 @@
                     Log.w(Constants.LOG_TAG, "Unexpected biometric result exists when " +
                             "autoSelect is preferred.")
                 }
-                // TODO(b/333445754) : Decide whether to propagate info on prompt launch
+                // TODO(b/333445754) : Change the fm option to false in qpr after discussion
                 if (biometricState.biometricResult != null) {
                     entryIntent?.putExtra(Constants.BIOMETRIC_AUTH_RESULT,
                         biometricState.biometricResult.biometricAuthenticationResult
                             .authenticationType)
+                    entryIntent?.putExtra(Constants.BIOMETRIC_FRAMEWORK_OPTION, true)
                 } else if (biometricState.biometricError != null){
                     entryIntent?.putExtra(Constants.BIOMETRIC_AUTH_ERROR_CODE,
                         biometricState.biometricError.errorCode)
                     entryIntent?.putExtra(Constants.BIOMETRIC_AUTH_ERROR_MESSAGE,
                         biometricState.biometricError.errorMessage)
+                    entryIntent?.putExtra(Constants.BIOMETRIC_FRAMEWORK_OPTION, true)
                 }
             }
             val intentSenderRequest = IntentSenderRequest.Builder(pendingIntent)
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
index b43b5f3..c35721c 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
@@ -38,7 +38,6 @@
 import com.android.credentialmanager.model.creation.CreateOptionInfo
 import com.android.credentialmanager.model.get.CredentialEntryInfo
 import com.android.credentialmanager.model.get.ProviderInfo
-import java.lang.Exception
 
 /**
  * Aggregates common display information used for the Biometric Flow.
@@ -121,11 +120,11 @@
     getBiometricCancellationSignal: () -> CancellationSignal,
     getRequestDisplayInfo: RequestDisplayInfo? = null,
     getProviderInfoList: List<ProviderInfo>? = null,
-    getProviderDisplayInfo: ProviderDisplayInfo? = null,
-) {
+    getProviderDisplayInfo: ProviderDisplayInfo? = null
+): Boolean {
     if (getBiometricPromptState() != BiometricPromptState.INACTIVE) {
         // Screen is already up, do not re-launch
-        return
+        return false
     }
     onBiometricPromptStateChange(BiometricPromptState.PENDING)
     val biometricDisplayInfo = validateAndRetrieveBiometricGetDisplayInfo(
@@ -137,7 +136,7 @@
 
     if (biometricDisplayInfo == null) {
         onBiometricFailureFallback(BiometricFlowType.GET)
-        return
+        return false
     }
 
     val callback: BiometricPrompt.AuthenticationCallback =
@@ -146,7 +145,7 @@
             getBiometricPromptState)
 
     Log.d(TAG, "The BiometricPrompt API call begins for Get.")
-    runBiometricFlow(context, biometricDisplayInfo, callback, openMoreOptionsPage,
+    return runBiometricFlow(context, biometricDisplayInfo, callback, openMoreOptionsPage,
         onBiometricFailureFallback, BiometricFlowType.GET, onCancelFlowAndFinish,
         getBiometricCancellationSignal)
 }
@@ -169,11 +168,11 @@
     getBiometricCancellationSignal: () -> CancellationSignal,
     createRequestDisplayInfo: com.android.credentialmanager.createflow
     .RequestDisplayInfo? = null,
-    createProviderInfo: EnabledProviderInfo? = null,
-) {
+    createProviderInfo: EnabledProviderInfo? = null
+): Boolean {
     if (getBiometricPromptState() != BiometricPromptState.INACTIVE) {
         // Screen is already up, do not re-launch
-        return
+        return false
     }
     onBiometricPromptStateChange(BiometricPromptState.PENDING)
     val biometricDisplayInfo = validateAndRetrieveBiometricCreateDisplayInfo(
@@ -184,7 +183,7 @@
 
     if (biometricDisplayInfo == null) {
         onBiometricFailureFallback(BiometricFlowType.CREATE)
-        return
+        return false
     }
 
     val callback: BiometricPrompt.AuthenticationCallback =
@@ -193,7 +192,7 @@
             getBiometricPromptState)
 
     Log.d(TAG, "The BiometricPrompt API call begins for Create.")
-    runBiometricFlow(context, biometricDisplayInfo, callback, openMoreOptionsPage,
+    return runBiometricFlow(context, biometricDisplayInfo, callback, openMoreOptionsPage,
         onBiometricFailureFallback, BiometricFlowType.CREATE, onCancelFlowAndFinish,
         getBiometricCancellationSignal)
 }
@@ -206,19 +205,19 @@
  * only device credentials are requested.
  */
 private fun runBiometricFlow(
-    context: Context,
-    biometricDisplayInfo: BiometricDisplayInfo,
-    callback: BiometricPrompt.AuthenticationCallback,
-    openMoreOptionsPage: () -> Unit,
-    onBiometricFailureFallback: (BiometricFlowType) -> Unit,
-    biometricFlowType: BiometricFlowType,
-    onCancelFlowAndFinish: () -> Unit,
-    getBiometricCancellationSignal: () -> CancellationSignal,
-) {
+        context: Context,
+        biometricDisplayInfo: BiometricDisplayInfo,
+        callback: BiometricPrompt.AuthenticationCallback,
+        openMoreOptionsPage: () -> Unit,
+        onBiometricFailureFallback: (BiometricFlowType) -> Unit,
+        biometricFlowType: BiometricFlowType,
+        onCancelFlowAndFinish: () -> Unit,
+        getBiometricCancellationSignal: () -> CancellationSignal
+): Boolean {
     try {
         if (!canCallBiometricPrompt(biometricDisplayInfo, context)) {
             onBiometricFailureFallback(biometricFlowType)
-            return
+            return false
         }
 
         val biometricPrompt = setupBiometricPrompt(context, biometricDisplayInfo,
@@ -231,7 +230,7 @@
         val cryptoOpId = getCryptoOpId(biometricDisplayInfo)
         if (cryptoOpId != null) {
             biometricPrompt.authenticate(
-                BiometricPrompt.CryptoObject(cryptoOpId.toLong()),
+                BiometricPrompt.CryptoObject(cryptoOpId),
                 cancellationSignal, executor, callback)
         } else {
             biometricPrompt.authenticate(cancellationSignal, executor, callback)
@@ -239,10 +238,12 @@
     } catch (e: IllegalArgumentException) {
         Log.w(TAG, "Calling the biometric prompt API failed with: /n${e.localizedMessage}\n")
         onBiometricFailureFallback(biometricFlowType)
+        return false
     }
+    return true
 }
 
-private fun getCryptoOpId(biometricDisplayInfo: BiometricDisplayInfo): Int? {
+private fun getCryptoOpId(biometricDisplayInfo: BiometricDisplayInfo): Long? {
     return biometricDisplayInfo.biometricRequestInfo.opId
 }
 
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt
index 3c80113..cb089ad 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt
@@ -22,9 +22,12 @@
         const val BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS =
             "androidx.credentials.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED"
         const val IS_AUTO_SELECTED_KEY = "IS_AUTO_SELECTED"
-        // TODO(b/333445772) : Qualify error codes fully for propagation
-        const val BIOMETRIC_AUTH_RESULT = "BIOMETRIC_AUTH_RESULT"
-        const val BIOMETRIC_AUTH_ERROR_CODE = "BIOMETRIC_AUTH_ERROR_CODE"
-        const val BIOMETRIC_AUTH_ERROR_MESSAGE = "BIOMETRIC_AUTH_ERROR_MESSAGE"
+        const val BIOMETRIC_AUTH_RESULT = "androidx.credentials.provider.BIOMETRIC_AUTH_RESULT"
+        const val BIOMETRIC_AUTH_ERROR_CODE =
+                "androidx.credentials.provider.BIOMETRIC_AUTH_ERROR_CODE"
+        const val BIOMETRIC_AUTH_ERROR_MESSAGE =
+                "androidx.credentials.provider.BIOMETRIC_AUTH_ERROR_MESSAGE"
+        const val BIOMETRIC_FRAMEWORK_OPTION =
+                "androidx.credentials.provider.BIOMETRIC_FRAMEWORK_OPTION"
     }
 }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 7d61f73..4993a1f 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -123,7 +123,8 @@
                                 onBiometricPromptStateChange =
                                 viewModel::onBiometricPromptStateChange,
                                 getBiometricCancellationSignal =
-                                viewModel::getBiometricCancellationSignal
+                                viewModel::getBiometricCancellationSignal,
+                                onLog = { viewModel.logUiEvent(it) },
                             )
                         CreateScreenState.MORE_OPTIONS_SELECTION_ONLY -> MoreOptionsSelectionCard(
                                 requestDisplayInfo = createCredentialUiState.requestDisplayInfo,
@@ -642,12 +643,13 @@
     getBiometricPromptState: () -> BiometricPromptState,
     onBiometricPromptStateChange: (BiometricPromptState) -> Unit,
     getBiometricCancellationSignal: () -> CancellationSignal,
+    onLog: @Composable (UiEventEnum) -> Unit
 ) {
     if (biometricEntry == null) {
         fallbackToOriginalFlow(BiometricFlowType.CREATE)
         return
     }
-    runBiometricFlowForCreate(
+    val biometricFlowCalled = runBiometricFlowForCreate(
         biometricEntry = biometricEntry,
         context = LocalContext.current,
         openMoreOptionsPage = onMoreOptionSelected,
@@ -659,6 +661,9 @@
         createProviderInfo = enabledProviderInfo,
         onBiometricFailureFallback = fallbackToOriginalFlow,
         onIllegalStateAndFinish = onIllegalScreenStateAndFinish,
-        getBiometricCancellationSignal = getBiometricCancellationSignal,
+        getBiometricCancellationSignal = getBiometricCancellationSignal
     )
+    if (biometricFlowCalled) {
+        onLog(CreateCredentialEvent.CREDMAN_CREATE_CRED_BIOMETRIC_FLOW_LAUNCHED)
+    }
 }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index ba61b90..517ad00 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -166,7 +166,8 @@
                                 onBiometricPromptStateChange =
                                 viewModel::onBiometricPromptStateChange,
                                 getBiometricCancellationSignal =
-                                viewModel::getBiometricCancellationSignal
+                                viewModel::getBiometricCancellationSignal,
+                                onLog = { viewModel.logUiEvent(it) },
                             )
                         } else if (credmanBiometricApiEnabled() &&
                                 getCredentialUiState.currentScreenState
@@ -260,12 +261,13 @@
     getBiometricPromptState: () -> BiometricPromptState,
     onBiometricPromptStateChange: (BiometricPromptState) -> Unit,
     getBiometricCancellationSignal: () -> CancellationSignal,
+    onLog: @Composable (UiEventEnum) -> Unit,
 ) {
     if (biometricEntry == null) {
         fallbackToOriginalFlow(BiometricFlowType.GET)
         return
     }
-    runBiometricFlowForGet(
+    val biometricFlowCalled = runBiometricFlowForGet(
         biometricEntry = biometricEntry,
         context = LocalContext.current,
         openMoreOptionsPage = onMoreOptionSelected,
@@ -280,6 +282,9 @@
         onBiometricFailureFallback = fallbackToOriginalFlow,
         getBiometricCancellationSignal = getBiometricCancellationSignal
     )
+    if (biometricFlowCalled) {
+        onLog(GetCredentialEvent.CREDMAN_GET_CRED_BIOMETRIC_FLOW_LAUNCHED)
+    }
 }
 
 /** Draws the primary credential selection page, used in Android U. */
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/logging/CreateCredentialEvent.kt b/packages/CredentialManager/src/com/android/credentialmanager/logging/CreateCredentialEvent.kt
index daa42be..dac25fa 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/logging/CreateCredentialEvent.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/logging/CreateCredentialEvent.kt
@@ -52,7 +52,10 @@
     CREDMAN_CREATE_CRED_EXTERNAL_ONLY_SELECTION(1327),
 
     @UiEvent(doc = "The more about passkeys intro card is visible on screen.")
-    CREDMAN_CREATE_CRED_MORE_ABOUT_PASSKEYS_INTRO(1328);
+    CREDMAN_CREATE_CRED_MORE_ABOUT_PASSKEYS_INTRO(1328),
+
+    @UiEvent(doc = "The single tap biometric flow is launched.")
+    CREDMAN_CREATE_CRED_BIOMETRIC_FLOW_LAUNCHED(1800);
 
     override fun getId(): Int {
         return this.id
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/logging/GetCredentialEvent.kt b/packages/CredentialManager/src/com/android/credentialmanager/logging/GetCredentialEvent.kt
index 8de8895..8870f28 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/logging/GetCredentialEvent.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/logging/GetCredentialEvent.kt
@@ -54,7 +54,10 @@
     CREDMAN_GET_CRED_PRIMARY_SELECTION_CARD(1341),
 
     @UiEvent(doc = "The all sign in option card is visible on screen.")
-    CREDMAN_GET_CRED_ALL_SIGN_IN_OPTION_CARD(1342);
+    CREDMAN_GET_CRED_ALL_SIGN_IN_OPTION_CARD(1342),
+
+    @UiEvent(doc = "The single tap biometric flow is launched.")
+    CREDMAN_GET_CRED_BIOMETRIC_FLOW_LAUNCHED(1801);
 
     override fun getId(): Int {
         return this.id
diff --git a/packages/CredentialManager/wear/robotests/Android.bp b/packages/CredentialManager/wear/robotests/Android.bp
index c0a1822..589a3d6 100644
--- a/packages/CredentialManager/wear/robotests/Android.bp
+++ b/packages/CredentialManager/wear/robotests/Android.bp
@@ -25,4 +25,5 @@
     ],
     java_resource_dirs: ["config"],
     upstream: true,
+    strict_mode: false,
 }
diff --git a/packages/DynamicSystemInstallationService/Android.bp b/packages/DynamicSystemInstallationService/Android.bp
index b8f54b3..ae69019 100644
--- a/packages/DynamicSystemInstallationService/Android.bp
+++ b/packages/DynamicSystemInstallationService/Android.bp
@@ -30,10 +30,6 @@
     certificate: "platform",
     privileged: true,
     platform_apis: true,
-
-    optimize: {
-        enabled: false,
-    },
 }
 
 java_library {
diff --git a/packages/InputDevices/res/values-af/strings.xml b/packages/InputDevices/res/values-af/strings.xml
index 9369140..7e2561f 100644
--- a/packages/InputDevices/res/values-af/strings.xml
+++ b/packages/InputDevices/res/values-af/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgies"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thais (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-am/strings.xml b/packages/InputDevices/res/values-am/strings.xml
index 16f6437..3053c44 100644
--- a/packages/InputDevices/res/values-am/strings.xml
+++ b/packages/InputDevices/res/values-am/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ጂዮርጂያኛ"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"ታይላንድኛ (ኬድማኒ)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ታይላንድኛ (ፓታሾት)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ar/strings.xml b/packages/InputDevices/res/values-ar/strings.xml
index 93223ba..fe8f59c 100644
--- a/packages/InputDevices/res/values-ar/strings.xml
+++ b/packages/InputDevices/res/values-ar/strings.xml
@@ -52,4 +52,6 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"الجورجية"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"‏التايلاندية (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"‏التايلاندية (Pattachote)"</string>
+    <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"الصربية (اللاتينية)"</string>
+    <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"لغة الجبل الأسود (اللاتينية)"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-as/strings.xml b/packages/InputDevices/res/values-as/strings.xml
index c57b591..15aa34d 100644
--- a/packages/InputDevices/res/values-as/strings.xml
+++ b/packages/InputDevices/res/values-as/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"থাই (কেডমানি)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"থাই (পাটাচ’টে)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-az/strings.xml b/packages/InputDevices/res/values-az/strings.xml
index 9c6bdb3..765d55b 100644
--- a/packages/InputDevices/res/values-az/strings.xml
+++ b/packages/InputDevices/res/values-az/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gürcü"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tay (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tay (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-b+sr+Latn/strings.xml b/packages/InputDevices/res/values-b+sr+Latn/strings.xml
index 80ecff5..9b52c34 100644
--- a/packages/InputDevices/res/values-b+sr+Latn/strings.xml
+++ b/packages/InputDevices/res/values-b+sr+Latn/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruzijska"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"tajski (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"tajski (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-be/strings.xml b/packages/InputDevices/res/values-be/strings.xml
index c5aa66f..697ab63 100644
--- a/packages/InputDevices/res/values-be/strings.xml
+++ b/packages/InputDevices/res/values-be/strings.xml
@@ -52,4 +52,6 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Грузінская"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Тайская (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Тайская (Патачотэ)"</string>
+    <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Сербская (лацініца)"</string>
+    <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Чарнагорская (лацініца)"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-bg/strings.xml b/packages/InputDevices/res/values-bg/strings.xml
index 1260d6a..4d70bf5 100644
--- a/packages/InputDevices/res/values-bg/strings.xml
+++ b/packages/InputDevices/res/values-bg/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"грузински"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"тайландски (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"тайландски (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-bn/strings.xml b/packages/InputDevices/res/values-bn/strings.xml
index a038da9..7c430d3 100644
--- a/packages/InputDevices/res/values-bn/strings.xml
+++ b/packages/InputDevices/res/values-bn/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"জর্জিয়ান"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"থাই (কেডমানি)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"থাই (পাট্টাচোটে)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-bs/strings.xml b/packages/InputDevices/res/values-bs/strings.xml
index 12e93bc..c47dad3 100644
--- a/packages/InputDevices/res/values-bs/strings.xml
+++ b/packages/InputDevices/res/values-bs/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruzijski"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"tajlandski (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"tajlandski (pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ca/strings.xml b/packages/InputDevices/res/values-ca/strings.xml
index 8a1e059..fe5a092 100644
--- a/packages/InputDevices/res/values-ca/strings.xml
+++ b/packages/InputDevices/res/values-ca/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgià"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tai (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tai (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-cs/strings.xml b/packages/InputDevices/res/values-cs/strings.xml
index 9ee17e1..4e3416c 100644
--- a/packages/InputDevices/res/values-cs/strings.xml
+++ b/packages/InputDevices/res/values-cs/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruzínština"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"thajština (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"thajština (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-da/strings.xml b/packages/InputDevices/res/values-da/strings.xml
index db75d3e..c263224 100644
--- a/packages/InputDevices/res/values-da/strings.xml
+++ b/packages/InputDevices/res/values-da/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgisk"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-de/strings.xml b/packages/InputDevices/res/values-de/strings.xml
index 3db695e..5876891 100644
--- a/packages/InputDevices/res/values-de/strings.xml
+++ b/packages/InputDevices/res/values-de/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgisch"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thailändisch (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thailändisch (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-el/strings.xml b/packages/InputDevices/res/values-el/strings.xml
index cb7aa2c..fb34edd 100644
--- a/packages/InputDevices/res/values-el/strings.xml
+++ b/packages/InputDevices/res/values-el/strings.xml
@@ -52,4 +52,6 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Γεωργιανά"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Ταϊλανδικά (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Ταϊλανδικά (Pattachote)"</string>
+    <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Σερβικά (Λατινικά)"</string>
+    <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Μαυροβουνιακά (Λατινικά)"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-en-rAU/strings.xml b/packages/InputDevices/res/values-en-rAU/strings.xml
index d113201..356ebd4 100644
--- a/packages/InputDevices/res/values-en-rAU/strings.xml
+++ b/packages/InputDevices/res/values-en-rAU/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-en-rCA/strings.xml b/packages/InputDevices/res/values-en-rCA/strings.xml
index cae7f00..1d7ba3d 100644
--- a/packages/InputDevices/res/values-en-rCA/strings.xml
+++ b/packages/InputDevices/res/values-en-rCA/strings.xml
@@ -52,4 +52,6 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string>
+    <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbian (Latin)"</string>
+    <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrin (Latin)"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-en-rGB/strings.xml b/packages/InputDevices/res/values-en-rGB/strings.xml
index d113201..356ebd4 100644
--- a/packages/InputDevices/res/values-en-rGB/strings.xml
+++ b/packages/InputDevices/res/values-en-rGB/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-en-rIN/strings.xml b/packages/InputDevices/res/values-en-rIN/strings.xml
index d113201..356ebd4 100644
--- a/packages/InputDevices/res/values-en-rIN/strings.xml
+++ b/packages/InputDevices/res/values-en-rIN/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-en-rXC/strings.xml b/packages/InputDevices/res/values-en-rXC/strings.xml
index 71c84da..a231d4c 100644
--- a/packages/InputDevices/res/values-en-rXC/strings.xml
+++ b/packages/InputDevices/res/values-en-rXC/strings.xml
@@ -52,4 +52,6 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‏‎‏‏‏‎‎‏‎‏‎‎‏‎‎‏‎‎‎‏‏‎‎‎‏‎‏‏‏‎‎‎‎‏‏‏‎‏‎‏‏‏‏‏‏‎‎Georgian‎‏‎‎‏‎"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‎‎‎‏‏‎‏‏‏‏‏‎‎‎‏‎‎‏‎‏‎‎‎‏‎‏‎‎‎‏‏‏‏‎‎‎‎‏‏‎‎‎‏‎‏‎‎‏‎‏‏‎‏‎‏‎‎Thai (Kedmanee)‎‏‎‎‏‎"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‏‏‏‎‎‎‏‎‎‏‎‎‏‏‎‎‎‏‎‎‏‎‎‏‏‎‎‏‎‏‎‏‎‏‎‏‎‏‎‏‎‏‏‏‏‏‎‏‎‏‏‎‏‎Thai (Pattachote)‎‏‎‎‏‎"</string>
+    <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‏‏‎‏‎‏‏‏‎‏‏‎‎‏‏‏‎‎‎‎‏‏‏‎‎‏‏‎‎‏‎‏‎‎‏‏‏‎‎‏‏‏‏‏‎‎‏‎‏‏‎‏‎‏‏‎Serbian (Latin)‎‏‎‎‏‎"</string>
+    <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‎‎‏‏‏‏‎‎‎‏‎‏‎‎‎‏‎‎‏‏‎‏‎‏‏‎‎‏‎‏‏‎‏‏‏‎‎‏‏‏‎‎‏‎Montenegrin (Latin)‎‏‎‎‏‎"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-es-rUS/strings.xml b/packages/InputDevices/res/values-es-rUS/strings.xml
index 7490f7d..c20d928 100644
--- a/packages/InputDevices/res/values-es-rUS/strings.xml
+++ b/packages/InputDevices/res/values-es-rUS/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tailandés (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tailandés (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-es/strings.xml b/packages/InputDevices/res/values-es/strings.xml
index 22b8cda..39905de 100644
--- a/packages/InputDevices/res/values-es/strings.xml
+++ b/packages/InputDevices/res/values-es/strings.xml
@@ -52,4 +52,6 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tailandés (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tailandés (Pattachote)"</string>
+    <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbio (latino)"</string>
+    <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrino (latino)"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-et/strings.xml b/packages/InputDevices/res/values-et/strings.xml
index 34fd3d7..f2d4340 100644
--- a/packages/InputDevices/res/values-et/strings.xml
+++ b/packages/InputDevices/res/values-et/strings.xml
@@ -52,4 +52,6 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruusia"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"tai (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tai (Pattachote)"</string>
+    <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbia (ladina)"</string>
+    <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegro (ladina)"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-eu/strings.xml b/packages/InputDevices/res/values-eu/strings.xml
index 15535fd..57af1f7 100644
--- a/packages/InputDevices/res/values-eu/strings.xml
+++ b/packages/InputDevices/res/values-eu/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiarra"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thailandiarra (kedmanee-a)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thailandiarra (pattachote-a)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-fa/strings.xml b/packages/InputDevices/res/values-fa/strings.xml
index 11280dd..6ab8411 100644
--- a/packages/InputDevices/res/values-fa/strings.xml
+++ b/packages/InputDevices/res/values-fa/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"گرجستانی"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"تایلندی (کدمانی)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"تایلندی (پاتاچوته)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-fi/strings.xml b/packages/InputDevices/res/values-fi/strings.xml
index 6c6d4cf..2c69b29 100644
--- a/packages/InputDevices/res/values-fi/strings.xml
+++ b/packages/InputDevices/res/values-fi/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"georgia"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"thai (kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"thai (pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-fr-rCA/strings.xml b/packages/InputDevices/res/values-fr-rCA/strings.xml
index 5c931cf..a4656ff 100644
--- a/packages/InputDevices/res/values-fr-rCA/strings.xml
+++ b/packages/InputDevices/res/values-fr-rCA/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Géorgien"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thaï (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thaï (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-fr/strings.xml b/packages/InputDevices/res/values-fr/strings.xml
index 1323675..76c4815 100644
--- a/packages/InputDevices/res/values-fr/strings.xml
+++ b/packages/InputDevices/res/values-fr/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Géorgien"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thaï (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thaï (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-gl/strings.xml b/packages/InputDevices/res/values-gl/strings.xml
index cedff5b..133fbf7 100644
--- a/packages/InputDevices/res/values-gl/strings.xml
+++ b/packages/InputDevices/res/values-gl/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Xeorxiano"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tailandés (kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tailandés (pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-gu/strings.xml b/packages/InputDevices/res/values-gu/strings.xml
index cbd4c40..a3c98ae 100644
--- a/packages/InputDevices/res/values-gu/strings.xml
+++ b/packages/InputDevices/res/values-gu/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"જ્યોર્જિઅન"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"થાઇ (કેડમાની)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"થાઇ (પટ્ટાશોટે)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-hi/strings.xml b/packages/InputDevices/res/values-hi/strings.xml
index 7e3df82..fafc42d 100644
--- a/packages/InputDevices/res/values-hi/strings.xml
+++ b/packages/InputDevices/res/values-hi/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"जॉर्जियन कीबोर्ड का लेआउट"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"थाई (केडमेनी)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"थाई (पटैचोटे)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-hr/strings.xml b/packages/InputDevices/res/values-hr/strings.xml
index ba3dc51..d8e7ec4 100644
--- a/packages/InputDevices/res/values-hr/strings.xml
+++ b/packages/InputDevices/res/values-hr/strings.xml
@@ -3,53 +3,57 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="8016145283189546017">"Uređaji za unos"</string>
     <string name="keyboard_layouts_label" msgid="6688773268302087545">"Android tipkovnica"</string>
-    <string name="keyboard_layout_english_uk_label" msgid="6664258463319999632">"engleska (UK)"</string>
-    <string name="keyboard_layout_english_us_label" msgid="8994890249649106291">"engleska (SAD)"</string>
-    <string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"engleska (SAD), međunarodna"</string>
-    <string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"engleska (SAD), Colemakov raspored"</string>
-    <string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"engleska (SAD), Dvorakov raspored"</string>
-    <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"Engleska (SAD), raspored Workman"</string>
-    <string name="keyboard_layout_german_label" msgid="8451565865467909999">"njemačka"</string>
-    <string name="keyboard_layout_french_label" msgid="813450119589383723">"francuska"</string>
+    <string name="keyboard_layout_english_uk_label" msgid="6664258463319999632">"engleski (UK)"</string>
+    <string name="keyboard_layout_english_us_label" msgid="8994890249649106291">"engleski (SAD)"</string>
+    <string name="keyboard_layout_english_us_intl" msgid="3705168594034233583">"engleski (SAD), međunarodni raspored"</string>
+    <string name="keyboard_layout_english_us_colemak_label" msgid="4194969610343455380">"engleski (SAD), Colemakov raspored"</string>
+    <string name="keyboard_layout_english_us_dvorak_label" msgid="793528923171145202">"engleski (SAD), Dvorakov raspored"</string>
+    <string name="keyboard_layout_english_us_workman_label" msgid="2944541595262173111">"engleski (SAD), raspored Workman"</string>
+    <string name="keyboard_layout_german_label" msgid="8451565865467909999">"njemački"</string>
+    <string name="keyboard_layout_french_label" msgid="813450119589383723">"francuski"</string>
     <string name="keyboard_layout_french_ca_label" msgid="365352601060604832">"francuska (Kanada)"</string>
-    <string name="keyboard_layout_russian_label" msgid="8724879775815042968">"ruska"</string>
-    <string name="keyboard_layout_russian_mac_label" msgid="3795866869038264796">"ruska, raspored Maca"</string>
-    <string name="keyboard_layout_spanish_label" msgid="7091555148131908240">"španjolska"</string>
-    <string name="keyboard_layout_swiss_french_label" msgid="4659191025396371684">"švicarsko-francuska"</string>
-    <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"švicarsko-njemačka"</string>
-    <string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgijska"</string>
-    <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bugarska"</string>
-    <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"bugarska (fonetska)"</string>
-    <string name="keyboard_layout_italian" msgid="6497079660449781213">"talijanska"</string>
-    <string name="keyboard_layout_danish" msgid="8036432066627127851">"danska"</string>
-    <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norveška"</string>
-    <string name="keyboard_layout_swedish" msgid="732959109088479351">"švedska"</string>
-    <string name="keyboard_layout_finnish" msgid="5585659438924315466">"finska"</string>
-    <string name="keyboard_layout_croatian" msgid="4172229471079281138">"hrvatska"</string>
-    <string name="keyboard_layout_czech" msgid="1349256901452975343">"češka"</string>
-    <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"Češka QWERTY tipkovnica"</string>
-    <string name="keyboard_layout_estonian" msgid="8775830985185665274">"estonska"</string>
-    <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"mađarska"</string>
-    <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"islandska"</string>
-    <string name="keyboard_layout_brazilian" msgid="5117896443147781939">"brazilska"</string>
-    <string name="keyboard_layout_portuguese" msgid="2888198587329660305">"portugalska"</string>
-    <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovačka"</string>
-    <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovenska"</string>
-    <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turska"</string>
+    <string name="keyboard_layout_russian_label" msgid="8724879775815042968">"ruski"</string>
+    <string name="keyboard_layout_russian_mac_label" msgid="3795866869038264796">"ruski, raspored na Macu"</string>
+    <string name="keyboard_layout_spanish_label" msgid="7091555148131908240">"španjolski"</string>
+    <string name="keyboard_layout_swiss_french_label" msgid="4659191025396371684">"francuski (Švicarska)"</string>
+    <string name="keyboard_layout_swiss_german_label" msgid="2305520941993314258">"njemački (Švicarska)"</string>
+    <string name="keyboard_layout_belgian" msgid="2011984572838651558">"belgijski raspored"</string>
+    <string name="keyboard_layout_bulgarian" msgid="8951224309972028398">"bugarski"</string>
+    <string name="keyboard_layout_bulgarian_phonetic" msgid="7568914730360106653">"bugarski (fonetski)"</string>
+    <string name="keyboard_layout_italian" msgid="6497079660449781213">"talijanski"</string>
+    <string name="keyboard_layout_danish" msgid="8036432066627127851">"danski"</string>
+    <string name="keyboard_layout_norwegian" msgid="9090097917011040937">"norveški"</string>
+    <string name="keyboard_layout_swedish" msgid="732959109088479351">"švedski"</string>
+    <string name="keyboard_layout_finnish" msgid="5585659438924315466">"finski"</string>
+    <string name="keyboard_layout_croatian" msgid="4172229471079281138">"hrvatski"</string>
+    <string name="keyboard_layout_czech" msgid="1349256901452975343">"češki"</string>
+    <string name="keyboard_layout_czech_qwerty" msgid="3331402534128515501">"češki (QWERTY tipkovnica)"</string>
+    <string name="keyboard_layout_estonian" msgid="8775830985185665274">"estonski"</string>
+    <string name="keyboard_layout_hungarian" msgid="4154963661406035109">"mađarski"</string>
+    <string name="keyboard_layout_icelandic" msgid="5836645650912489642">"islandski"</string>
+    <string name="keyboard_layout_brazilian" msgid="5117896443147781939">"portugalski (Brazil)"</string>
+    <string name="keyboard_layout_portuguese" msgid="2888198587329660305">"portugalski"</string>
+    <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovački"</string>
+    <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovenski"</string>
+    <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turski"</string>
     <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turski F"</string>
-    <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinska"</string>
+    <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinski"</string>
     <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arapski"</string>
     <string name="keyboard_layout_greek" msgid="7289253560162386040">"grčki"</string>
     <string name="keyboard_layout_hebrew" msgid="7241473985890173812">"hebrejski"</string>
     <string name="keyboard_layout_lithuanian" msgid="6943110873053106534">"litavski"</string>
     <string name="keyboard_layout_spanish_latin" msgid="5690539836069535697">"španjolski (Latinska Amerika)"</string>
-    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"latvijska"</string>
+    <string name="keyboard_layout_latvian" msgid="4405417142306250595">"latvijski"</string>
     <string name="keyboard_layout_persian" msgid="3920643161015888527">"perzijski"</string>
     <string name="keyboard_layout_azerbaijani" msgid="7315895417176467567">"azerbajdžanski"</string>
     <string name="keyboard_layout_polish" msgid="1121588624094925325">"poljski"</string>
     <string name="keyboard_layout_belarusian" msgid="7619281752698687588">"bjeloruski"</string>
-    <string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolski"</string>
-    <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gruzijska"</string>
-    <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"tajlandski (kedmanee)"</string>
-    <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tajski (pattachote)"</string>
+    <string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongolski"</string>
+    <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruzijski"</string>
+    <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"tajski (kedmanee)"</string>
+    <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"tajski (pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-hu/strings.xml b/packages/InputDevices/res/values-hu/strings.xml
index c42e009..88c532e 100644
--- a/packages/InputDevices/res/values-hu/strings.xml
+++ b/packages/InputDevices/res/values-hu/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"grúz"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"thai (kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"thai (pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-hy/strings.xml b/packages/InputDevices/res/values-hy/strings.xml
index d85cf9d..ef4128e 100644
--- a/packages/InputDevices/res/values-hy/strings.xml
+++ b/packages/InputDevices/res/values-hy/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"վրացերեն"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"թայերեն (քեդմանի)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"թայերեն (պատաչոտ)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml
index d504540..d4f024a 100644
--- a/packages/InputDevices/res/values-in/strings.xml
+++ b/packages/InputDevices/res/values-in/strings.xml
@@ -52,4 +52,6 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgia"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string>
+    <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbia (Latin)"</string>
+    <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegro (Latin)"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-is/strings.xml b/packages/InputDevices/res/values-is/strings.xml
index 637874c..680c4e3 100644
--- a/packages/InputDevices/res/values-is/strings.xml
+++ b/packages/InputDevices/res/values-is/strings.xml
@@ -52,4 +52,6 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"georgíska"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Taílenskt (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Taílenskt (Pattachote)"</string>
+    <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbneska (latneskt)"</string>
+    <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Svartfellska (latneskt)"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-it/strings.xml b/packages/InputDevices/res/values-it/strings.xml
index eed8316..97a2359 100644
--- a/packages/InputDevices/res/values-it/strings.xml
+++ b/packages/InputDevices/res/values-it/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-iw/strings.xml b/packages/InputDevices/res/values-iw/strings.xml
index 8cfe2cb..0f7a341 100644
--- a/packages/InputDevices/res/values-iw/strings.xml
+++ b/packages/InputDevices/res/values-iw/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"גיאורגית"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"‏תאית (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"‏תאית (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ja/strings.xml b/packages/InputDevices/res/values-ja/strings.xml
index d1b334b..f6cfd43 100644
--- a/packages/InputDevices/res/values-ja/strings.xml
+++ b/packages/InputDevices/res/values-ja/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ジョージア語"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"タイ語(Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"タイ語(Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ka/strings.xml b/packages/InputDevices/res/values-ka/strings.xml
index 8928f68..4eebe6b 100644
--- a/packages/InputDevices/res/values-ka/strings.xml
+++ b/packages/InputDevices/res/values-ka/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ქართული"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"ტაილანდური (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ტაილანდური (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-kk/strings.xml b/packages/InputDevices/res/values-kk/strings.xml
index cf3d3ca..b1ca40a 100644
--- a/packages/InputDevices/res/values-kk/strings.xml
+++ b/packages/InputDevices/res/values-kk/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Грузин"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Тай (кедмани)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Тай (паттачот)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-km/strings.xml b/packages/InputDevices/res/values-km/strings.xml
index 53eb6f5..b8571ec 100644
--- a/packages/InputDevices/res/values-km/strings.xml
+++ b/packages/InputDevices/res/values-km/strings.xml
@@ -52,4 +52,6 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ហ្សក​ហ្ស៊ី"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"ថៃ (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ថៃ (ប៉ាតាឈោត)"</string>
+    <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"ស៊ែប៊ី (ឡាតាំង)"</string>
+    <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"ម៉ុងតេណេហ្គ្រោ (ឡាតាំង)"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-kn/strings.xml b/packages/InputDevices/res/values-kn/strings.xml
index c743a6e..94d65bd 100644
--- a/packages/InputDevices/res/values-kn/strings.xml
+++ b/packages/InputDevices/res/values-kn/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ಜಾರ್ಜಿಯನ್"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"ಥಾಯ್ (ಕೆಡ್‍ಮನೀ)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ಥಾಯ್ (ಪಟ್ಟಚೋಟ್)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ko/strings.xml b/packages/InputDevices/res/values-ko/strings.xml
index 0e375dd..fa2d9da 100644
--- a/packages/InputDevices/res/values-ko/strings.xml
+++ b/packages/InputDevices/res/values-ko/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"조지아어"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"태국어(Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"태국어(Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ky/strings.xml b/packages/InputDevices/res/values-ky/strings.xml
index dad5c91..9434840 100644
--- a/packages/InputDevices/res/values-ky/strings.xml
+++ b/packages/InputDevices/res/values-ky/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Грузинче"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Тайча (Kedmanee баскычтобу)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Тайча (Pattachote баскычтобу)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-lo/strings.xml b/packages/InputDevices/res/values-lo/strings.xml
index 0794bde..95a8903 100644
--- a/packages/InputDevices/res/values-lo/strings.xml
+++ b/packages/InputDevices/res/values-lo/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ຈໍຈຽນ"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"ໄທ (ເກດມະນີ)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ໄທ (ປັດຕະໂຊຕິ)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-lt/strings.xml b/packages/InputDevices/res/values-lt/strings.xml
index 0cceec7..b9a3e20 100644
--- a/packages/InputDevices/res/values-lt/strings.xml
+++ b/packages/InputDevices/res/values-lt/strings.xml
@@ -52,4 +52,6 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gruzinų"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tajų („Kedmanee“)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tajų („Pattachote“)"</string>
+    <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serbų (lotynų rašmenys)"</string>
+    <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Juodkalniečių (lotynų rašmenys)"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-lv/strings.xml b/packages/InputDevices/res/values-lv/strings.xml
index 9b52854..3cd4da7 100644
--- a/packages/InputDevices/res/values-lv/strings.xml
+++ b/packages/InputDevices/res/values-lv/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gruzīnu"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Taju valoda (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Taju (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-mk/strings.xml b/packages/InputDevices/res/values-mk/strings.xml
index 4e8be46..b91fcc1 100644
--- a/packages/InputDevices/res/values-mk/strings.xml
+++ b/packages/InputDevices/res/values-mk/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"грузиски"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"тајландски (кедмани)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"тајландски (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ml/strings.xml b/packages/InputDevices/res/values-ml/strings.xml
index 4b2a5fd..408ae13 100644
--- a/packages/InputDevices/res/values-ml/strings.xml
+++ b/packages/InputDevices/res/values-ml/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ജോര്‍ജ്ജിയൻ"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"തായ് (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"തായ് (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-mn/strings.xml b/packages/InputDevices/res/values-mn/strings.xml
index a7a1799..2490d81 100644
--- a/packages/InputDevices/res/values-mn/strings.xml
+++ b/packages/InputDevices/res/values-mn/strings.xml
@@ -52,4 +52,6 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Гүрж"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Тай (кедмани)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Тай (паттачоте)"</string>
+    <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Серби (латин)"</string>
+    <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Монтенегро (латин)"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-mr/strings.xml b/packages/InputDevices/res/values-mr/strings.xml
index 5e4baa0..47cebf1 100644
--- a/packages/InputDevices/res/values-mr/strings.xml
+++ b/packages/InputDevices/res/values-mr/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"जॉर्जियन"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"थाई (केडमानी)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"थाई (पट्टाचोटे)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ms/strings.xml b/packages/InputDevices/res/values-ms/strings.xml
index 9e4c190..9a1c4f7 100644
--- a/packages/InputDevices/res/values-ms/strings.xml
+++ b/packages/InputDevices/res/values-ms/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Bahasa Georgia"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-my/strings.xml b/packages/InputDevices/res/values-my/strings.xml
index 5dbdc70..0510240 100644
--- a/packages/InputDevices/res/values-my/strings.xml
+++ b/packages/InputDevices/res/values-my/strings.xml
@@ -52,4 +52,6 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ဂျော်ဂျီယာ"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"ထိုင်း (ကတ်မနီး)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ထိုင်း (ပတ်တာချုတ်)"</string>
+    <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"ဆားဘီးယား (လက်တင်)"</string>
+    <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"မွန်တီနီဂရင်း (လက်တင်)"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-nb/strings.xml b/packages/InputDevices/res/values-nb/strings.xml
index 1e9af39..2545448 100644
--- a/packages/InputDevices/res/values-nb/strings.xml
+++ b/packages/InputDevices/res/values-nb/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgisk"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ne/strings.xml b/packages/InputDevices/res/values-ne/strings.xml
index ab22576..e85d615 100644
--- a/packages/InputDevices/res/values-ne/strings.xml
+++ b/packages/InputDevices/res/values-ne/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"जर्जियाली"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"थाई (केडमानी)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"थाई (पत्ताचोते)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-nl/strings.xml b/packages/InputDevices/res/values-nl/strings.xml
index d28ee9b..1893704 100644
--- a/packages/InputDevices/res/values-nl/strings.xml
+++ b/packages/InputDevices/res/values-nl/strings.xml
@@ -52,4 +52,6 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgisch"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string>
+    <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Servisch (Latijns)"</string>
+    <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Montenegrijns (Latijns)"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-or/strings.xml b/packages/InputDevices/res/values-or/strings.xml
index e92c155..8df615e 100644
--- a/packages/InputDevices/res/values-or/strings.xml
+++ b/packages/InputDevices/res/values-or/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ଜର୍ଜିଆନ୍"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"ଥାଇ (କେଡମାନି)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ଥାଇ (ପାଟ୍ଟାଚୋଟେ)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-pa/strings.xml b/packages/InputDevices/res/values-pa/strings.xml
index f766297..b0a140e 100644
--- a/packages/InputDevices/res/values-pa/strings.xml
+++ b/packages/InputDevices/res/values-pa/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ਜਾਰਜੀਆਈ"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"ਥਾਈ (ਕੇਦਮਨੀ)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ਥਾਈ (ਪੈਟਾਸ਼ੋਟੇ)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-pl/strings.xml b/packages/InputDevices/res/values-pl/strings.xml
index e202463..b76c0fe 100644
--- a/packages/InputDevices/res/values-pl/strings.xml
+++ b/packages/InputDevices/res/values-pl/strings.xml
@@ -52,4 +52,6 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruziński"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"tajski (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"tajski (Pattachote)"</string>
+    <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"serbski (alfabet łaciński)"</string>
+    <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"czarnogórski (alfabet łaciński)"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-pt-rBR/strings.xml b/packages/InputDevices/res/values-pt-rBR/strings.xml
index 4a0c3be..6fa852b 100644
--- a/packages/InputDevices/res/values-pt-rBR/strings.xml
+++ b/packages/InputDevices/res/values-pt-rBR/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tailandês (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tailandês (pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-pt-rPT/strings.xml b/packages/InputDevices/res/values-pt-rPT/strings.xml
index c54b620..b768467 100644
--- a/packages/InputDevices/res/values-pt-rPT/strings.xml
+++ b/packages/InputDevices/res/values-pt-rPT/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tailandês (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tailandês (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-pt/strings.xml b/packages/InputDevices/res/values-pt/strings.xml
index 4a0c3be..6fa852b 100644
--- a/packages/InputDevices/res/values-pt/strings.xml
+++ b/packages/InputDevices/res/values-pt/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiano"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tailandês (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tailandês (pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ro/strings.xml b/packages/InputDevices/res/values-ro/strings.xml
index d91635b..9dc2841 100644
--- a/packages/InputDevices/res/values-ro/strings.xml
+++ b/packages/InputDevices/res/values-ro/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgiană"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thailandeză (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thailandeză (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml
index da1a83a..9612717b 100644
--- a/packages/InputDevices/res/values-ru/strings.xml
+++ b/packages/InputDevices/res/values-ru/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"грузинский"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Тайский (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Тайский (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-si/strings.xml b/packages/InputDevices/res/values-si/strings.xml
index 97aed62..2151f44 100644
--- a/packages/InputDevices/res/values-si/strings.xml
+++ b/packages/InputDevices/res/values-si/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ජෝර්ජියානු"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"තායි (කෙඩ්මනී)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"තායි (පට්ටචෝටේ)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-sk/strings.xml b/packages/InputDevices/res/values-sk/strings.xml
index 6f387ad..c8b6021 100644
--- a/packages/InputDevices/res/values-sk/strings.xml
+++ b/packages/InputDevices/res/values-sk/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruzínske"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"thajčina (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"thajčina (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-sl/strings.xml b/packages/InputDevices/res/values-sl/strings.xml
index 32ca0ad..1e04ae1 100644
--- a/packages/InputDevices/res/values-sl/strings.xml
+++ b/packages/InputDevices/res/values-sl/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruzinščina"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"tajščina (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"tajščina (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-sq/strings.xml b/packages/InputDevices/res/values-sq/strings.xml
index c33ba4a..8ad13f4 100644
--- a/packages/InputDevices/res/values-sq/strings.xml
+++ b/packages/InputDevices/res/values-sq/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gjeorgjisht"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tajlandisht (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tajlandisht (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-sr/strings.xml b/packages/InputDevices/res/values-sr/strings.xml
index 0b434d7..28cd5ca 100644
--- a/packages/InputDevices/res/values-sr/strings.xml
+++ b/packages/InputDevices/res/values-sr/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"грузијска"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"тајски (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"тајски (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-sv/strings.xml b/packages/InputDevices/res/values-sv/strings.xml
index 3d08415..c24c300 100644
--- a/packages/InputDevices/res/values-sv/strings.xml
+++ b/packages/InputDevices/res/values-sv/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"georgiska"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"thailändska (pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-sw/strings.xml b/packages/InputDevices/res/values-sw/strings.xml
index 42714a5..0cf002e 100644
--- a/packages/InputDevices/res/values-sw/strings.xml
+++ b/packages/InputDevices/res/values-sw/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Kijojia"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Kithai (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Kitai (Kipatachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ta/strings.xml b/packages/InputDevices/res/values-ta/strings.xml
index f8bc751..87e9105 100644
--- a/packages/InputDevices/res/values-ta/strings.xml
+++ b/packages/InputDevices/res/values-ta/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ஜார்ஜியன்"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"தாய் (கேட்மேனி)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"தாய் (பட்டாசொட்டே)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-te/strings.xml b/packages/InputDevices/res/values-te/strings.xml
index 2c1c1f8..4cf1b14 100644
--- a/packages/InputDevices/res/values-te/strings.xml
+++ b/packages/InputDevices/res/values-te/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"జార్జియన్"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"థాయ్ (కెడ్మనీ)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"థాయ్ (పత్తచోత్)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-th/strings.xml b/packages/InputDevices/res/values-th/strings.xml
index 3b96226..88cf752 100644
--- a/packages/InputDevices/res/values-th/strings.xml
+++ b/packages/InputDevices/res/values-th/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"ภาษาจอร์เจีย"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"ไทย (เกษมณี)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"ไทย (ปัตตะโชติ)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-tl/strings.xml b/packages/InputDevices/res/values-tl/strings.xml
index f0cd0f8..787c851 100644
--- a/packages/InputDevices/res/values-tl/strings.xml
+++ b/packages/InputDevices/res/values-tl/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Thai (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-tr/strings.xml b/packages/InputDevices/res/values-tr/strings.xml
index a5c89d7..62360b5 100644
--- a/packages/InputDevices/res/values-tr/strings.xml
+++ b/packages/InputDevices/res/values-tr/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gürcüce"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tayca (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tayca (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-uk/strings.xml b/packages/InputDevices/res/values-uk/strings.xml
index dd3aab8..15b1a25 100644
--- a/packages/InputDevices/res/values-uk/strings.xml
+++ b/packages/InputDevices/res/values-uk/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Грузинська"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Тайська (кедмані)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Тайська (паттачоте)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-ur/strings.xml b/packages/InputDevices/res/values-ur/strings.xml
index 008cd10..d10c798 100644
--- a/packages/InputDevices/res/values-ur/strings.xml
+++ b/packages/InputDevices/res/values-ur/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"جارجیائی"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"تھائی (کیڈمینی)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"تھائی (پٹاچوٹے)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-uz/strings.xml b/packages/InputDevices/res/values-uz/strings.xml
index 2c1c4b0..0e80d71 100644
--- a/packages/InputDevices/res/values-uz/strings.xml
+++ b/packages/InputDevices/res/values-uz/strings.xml
@@ -52,4 +52,6 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Gruzin"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tay (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tay (Pattachote)"</string>
+    <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Serb (lotin)"</string>
+    <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Chernogor (lotin)"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-vi/strings.xml b/packages/InputDevices/res/values-vi/strings.xml
index b5a0b16b..5094a29 100644
--- a/packages/InputDevices/res/values-vi/strings.xml
+++ b/packages/InputDevices/res/values-vi/strings.xml
@@ -52,4 +52,6 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Tiếng Georgia"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Tiếng Thái (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Tiếng Thái (Pattachote)"</string>
+    <string name="keyboard_layout_serbian_latin" msgid="3128791759390046571">"Tiếng Serbia (Latinh)"</string>
+    <string name="keyboard_layout_montenegrin_latin" msgid="1467832503378949945">"Tiếng Montenegro (Latinh)"</string>
 </resources>
diff --git a/packages/InputDevices/res/values-zh-rCN/strings.xml b/packages/InputDevices/res/values-zh-rCN/strings.xml
index 97e75e6..5934e3b 100644
--- a/packages/InputDevices/res/values-zh-rCN/strings.xml
+++ b/packages/InputDevices/res/values-zh-rCN/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"格鲁吉亚语"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"泰语 (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"泰语 (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-zh-rHK/strings.xml b/packages/InputDevices/res/values-zh-rHK/strings.xml
index 45d4b4f..dbcfd1c 100644
--- a/packages/InputDevices/res/values-zh-rHK/strings.xml
+++ b/packages/InputDevices/res/values-zh-rHK/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"格魯吉亞文"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"泰文 (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"泰文 (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-zh-rTW/strings.xml b/packages/InputDevices/res/values-zh-rTW/strings.xml
index f0ea94b..c87f2ac 100644
--- a/packages/InputDevices/res/values-zh-rTW/strings.xml
+++ b/packages/InputDevices/res/values-zh-rTW/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"喬治亞文"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"泰文 (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"泰文 (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/InputDevices/res/values-zu/strings.xml b/packages/InputDevices/res/values-zu/strings.xml
index 079b841..f62afba 100644
--- a/packages/InputDevices/res/values-zu/strings.xml
+++ b/packages/InputDevices/res/values-zu/strings.xml
@@ -52,4 +52,8 @@
     <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string>
     <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Isi-Thai (Kedmanee)"</string>
     <string name="keyboard_layout_thai_pattachote" msgid="2547992342794252205">"Isi-Thai (Pattachote)"</string>
+    <!-- no translation found for keyboard_layout_serbian_latin (3128791759390046571) -->
+    <skip />
+    <!-- no translation found for keyboard_layout_montenegrin_latin (1467832503378949945) -->
+    <skip />
 </resources>
diff --git a/packages/PackageInstaller/res/values-bg/strings.xml b/packages/PackageInstaller/res/values-bg/strings.xml
index f6efdf6..b844054 100644
--- a/packages/PackageInstaller/res/values-bg/strings.xml
+++ b/packages/PackageInstaller/res/values-bg/strings.xml
@@ -63,7 +63,7 @@
     <string name="archive_application_text_all_users" msgid="3151229641681672580">"Да се архивира ли това приложение за всички потребители? Личните ви данни ще бъдат запазени"</string>
     <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Да се архивира ли това приложение в служебния ви потребителски профил? Личните ви данни ще бъдат запазени"</string>
     <string name="archive_application_text_user" msgid="2586558895535581451">"Да се архивира ли това приложение за <xliff:g id="USERNAME">%1$s</xliff:g>? Личните ви данни ще бъдат запазени"</string>
-    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Искате ли да архивирате това приложение от личното си пространство? Личните ви данни ще бъдат запазени"</string>
+    <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"Искате ли да архивирате това приложение от частното си пространство? Личните ви данни ще бъдат запазени"</string>
     <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Искате ли да деинсталирате това приложение за "<b>"всички"</b>" потребители? Приложението и данните му ще бъдат премахнати от "<b>"всички"</b>" потребители на устройството."</string>
     <string name="uninstall_application_text_user" msgid="498072714173920526">"Искате ли да деинсталирате това приложение за потребителя <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
     <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Искате ли да деинсталирате това приложение от служебния си потребителски профил?"</string>
@@ -72,7 +72,7 @@
     <string name="uninstall_keep_data" msgid="7002379587465487550">"Запазване на <xliff:g id="SIZE">%1$s</xliff:g> данни от приложението."</string>
     <string name="uninstall_application_text_current_user_clone_profile" msgid="835170400160011636">"Искате ли да изтриете това приложение?"</string>
     <string name="uninstall_application_text_with_clone_instance" msgid="6944473334273349036">"Искате ли да деинсталирате това приложение? Копието на <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g> също ще бъде изтрито."</string>
-    <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Искате ли да деинсталирате това приложение от личното си пространство?"</string>
+    <string name="uninstall_application_text_current_user_private_profile" msgid="867004464945674674">"Искате ли да деинсталирате това приложение от частното си пространство?"</string>
     <string name="uninstalling_notification_channel" msgid="840153394325714653">"Активни деинсталирания"</string>
     <string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Неуспешни деинсталирания"</string>
     <string name="uninstalling" msgid="8709566347688966845">"Деинсталира се..."</string>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index 59a511d..10e8246 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -311,18 +311,18 @@
                         broadcastIntent,
                         PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
 
-                try {
-                    // Delay committing the session by 100ms to fix a UI glitch while displaying the
-                    // Update-Owner change dialog on top of the Installing dialog
-                    new Handler(Looper.getMainLooper()).postDelayed(() -> {
+                // Delay committing the session by 100ms to fix a UI glitch while displaying the
+                // Update-Owner change dialog on top of the Installing dialog
+                new Handler(Looper.getMainLooper()).postDelayed(() -> {
+                    try {
                         session.commit(pendingIntent.getIntentSender());
-                    }, 100);
-                } catch (Exception e) {
-                    Log.e(LOG_TAG, "Cannot install package: ", e);
-                    launchFailure(PackageInstaller.STATUS_FAILURE,
-                        PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
-                    return;
-                }
+                    } catch (Exception e) {
+                        Log.e(LOG_TAG, "Cannot install package: ", e);
+                        launchFailure(PackageInstaller.STATUS_FAILURE,
+                            PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
+                        return;
+                    }
+                }, 100);
                 mCancelButton.setEnabled(false);
                 setFinishOnTouchOutside(false);
             } else {
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
index 3fea599..379dfe3 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -125,13 +125,14 @@
                 -1, callingUid) == PackageManager.PERMISSION_GRANTED;
         boolean isSystemDownloadsProvider = PackageUtil.getSystemDownloadsProviderInfo(
                                                 mPackageManager, callingUid) != null;
-        boolean isTrustedSource = false;
-        if (sourceInfo != null && sourceInfo.isPrivilegedApp()) {
-            isTrustedSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false) || (
-                callingUid != Process.INVALID_UID && checkPermission(
-                    Manifest.permission.INSTALL_PACKAGES, -1 /* pid */, callingUid)
-                    == PackageManager.PERMISSION_GRANTED);
-        }
+
+        boolean isPrivilegedAndKnown = (sourceInfo != null && sourceInfo.isPrivilegedApp()) &&
+            intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false);
+        boolean isInstallPkgPermissionGranted =
+            checkPermission(Manifest.permission.INSTALL_PACKAGES, /* pid= */ -1, callingUid)
+                    == PackageManager.PERMISSION_GRANTED;
+
+        boolean isTrustedSource = isPrivilegedAndKnown || isInstallPkgPermissionGranted;
 
         if (!isTrustedSource && !isSystemDownloadsProvider && !isDocumentsManager
                 && callingUid != Process.INVALID_UID) {
@@ -154,7 +155,7 @@
             mAbortInstall = true;
         }
 
-        checkDevicePolicyRestrictions();
+        checkDevicePolicyRestrictions(isTrustedSource);
 
         final String installerPackageNameFromIntent = getIntent().getStringExtra(
                 Intent.EXTRA_INSTALLER_PACKAGE_NAME);
@@ -304,12 +305,17 @@
         return callingUid == installerUid;
     }
 
-    private void checkDevicePolicyRestrictions() {
-        final String[] restrictions = new String[] {
-            UserManager.DISALLOW_INSTALL_APPS,
-            UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
-            UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY
-        };
+    private void checkDevicePolicyRestrictions(boolean isTrustedSource) {
+        String[] restrictions;
+        if(isTrustedSource) {
+            restrictions = new String[] { UserManager.DISALLOW_INSTALL_APPS };
+        } else {
+            restrictions =  new String[] {
+                UserManager.DISALLOW_INSTALL_APPS,
+                UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+                UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY
+            };
+        }
 
         final DevicePolicyManager dpm = getSystemService(DevicePolicyManager.class);
         for (String restriction : restrictions) {
diff --git a/packages/PrintSpooler/res/values-kk/strings.xml b/packages/PrintSpooler/res/values-kk/strings.xml
index 939e1b4..1755c7a 100644
--- a/packages/PrintSpooler/res/values-kk/strings.xml
+++ b/packages/PrintSpooler/res/values-kk/strings.xml
@@ -74,7 +74,7 @@
     <string name="enabled_services_title" msgid="7036986099096582296">"Қосылған қызметтер"</string>
     <string name="recommended_services_title" msgid="3799434882937956924">"Ұсынылған қызметтер"</string>
     <string name="disabled_services_title" msgid="7313253167968363211">"Өшірілген қызметтер"</string>
-    <string name="all_services_title" msgid="5578662754874906455">"Барлық қызметтер"</string>
+    <string name="all_services_title" msgid="5578662754874906455">"Барлық қызмет"</string>
     <plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
       <item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> принтерді табу үшін орнатыңыз</item>
       <item quantity="one"><xliff:g id="COUNT_0">%1$s</xliff:g> принтерді табу үшін орнатыңыз</item>
diff --git a/packages/SettingsLib/Color/res/values/colors.xml b/packages/SettingsLib/Color/res/values/colors.xml
index ef0dd1b..7097523 100644
--- a/packages/SettingsLib/Color/res/values/colors.xml
+++ b/packages/SettingsLib/Color/res/values/colors.xml
@@ -17,7 +17,6 @@
 
 <resources>
     <!-- Dynamic colors-->
-    <color name="settingslib_color_blue700">#0B57D0</color>
     <color name="settingslib_color_blue600">#1a73e8</color>
     <color name="settingslib_color_blue400">#669df6</color>
     <color name="settingslib_color_blue300">#8ab4f8</color>
@@ -63,4 +62,5 @@
     <color name="settingslib_color_cyan400">#4ecde6</color>
     <color name="settingslib_color_cyan300">#78d9ec</color>
     <color name="settingslib_color_cyan100">#cbf0f8</color>
+    <color name="settingslib_color_charcoal">#171717</color>
 </resources>
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt
index 8242347..b4a9172 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt
@@ -138,7 +138,7 @@
         private fun notifyBackupManager(key: Any?, reason: Int) {
             val name = storage.name
             // prefer not triggering backup immediately after restore
-            if (reason == ChangeReason.RESTORE) {
+            if (reason == DataChangeReason.RESTORE) {
                 Log.d(
                     LOG_TAG,
                     "Notify BackupManager dataChanged ignored for restore: storage=$name key=$key"
@@ -161,8 +161,8 @@
 
         fun notifyRestoreFinished() {
             when (storage) {
-                is KeyedObservable<*> -> storage.notifyChange(ChangeReason.RESTORE)
-                is Observable -> storage.notifyChange(ChangeReason.RESTORE)
+                is KeyedObservable<*> -> storage.notifyChange(DataChangeReason.RESTORE)
+                is Observable -> storage.notifyChange(DataChangeReason.RESTORE)
             }
         }
     }
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/DataChangeReason.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/DataChangeReason.kt
new file mode 100644
index 0000000..145fabe
--- /dev/null
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/DataChangeReason.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import androidx.annotation.IntDef
+
+/** The reason of data change. */
+@IntDef(
+    DataChangeReason.UNKNOWN,
+    DataChangeReason.UPDATE,
+    DataChangeReason.DELETE,
+    DataChangeReason.RESTORE,
+    DataChangeReason.SYNC_ACROSS_PROFILES,
+)
+@Retention(AnnotationRetention.SOURCE)
+annotation class DataChangeReason {
+    companion object {
+        /** Unknown reason of the change. */
+        const val UNKNOWN = 0
+        /** Data is updated. */
+        const val UPDATE = 1
+        /** Data is deleted. */
+        const val DELETE = 2
+        /** Data is restored from backup/restore framework. */
+        const val RESTORE = 3
+        /** Data is synced from another profile (e.g. personal profile to work profile). */
+        const val SYNC_ACROSS_PROFILES = 4
+    }
+}
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 3ed4d46..ede7c63 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
@@ -37,7 +37,7 @@
      * @param reason the reason of change
      * @see KeyedObservable.addObserver
      */
-    fun onKeyChanged(key: K, @ChangeReason reason: Int)
+    fun onKeyChanged(key: K, reason: Int)
 }
 
 /**
@@ -89,7 +89,7 @@
      *
      * @param reason reason of the change
      */
-    fun notifyChange(@ChangeReason reason: Int)
+    fun notifyChange(reason: Int)
 
     /**
      * Notifies observers that a change occurs on given key.
@@ -99,7 +99,7 @@
      * @param key key of the change
      * @param reason reason of the change
      */
-    fun notifyChange(key: K, @ChangeReason reason: Int)
+    fun notifyChange(key: K, reason: Int)
 }
 
 /** A thread safe implementation of [KeyedObservable]. */
@@ -141,7 +141,7 @@
         }
     }
 
-    override fun notifyChange(@ChangeReason reason: Int) {
+    override fun notifyChange(reason: Int) {
         // make a copy to avoid potential ConcurrentModificationException
         val observers = synchronized(observers) { observers.entries.toTypedArray() }
         val keyedObservers = synchronized(keyedObservers) { keyedObservers.copy() }
@@ -165,7 +165,7 @@
         return result
     }
 
-    override fun notifyChange(key: K, @ChangeReason reason: Int) {
+    override fun notifyChange(key: K, reason: Int) {
         // make a copy to avoid potential ConcurrentModificationException
         val observers = synchronized(observers) { observers.entries.toTypedArray() }
         val keyedObservers =
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/Observer.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/Observer.kt
index 6d0ca669..98d0f6e 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/Observer.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/Observer.kt
@@ -18,34 +18,9 @@
 
 import androidx.annotation.AnyThread
 import androidx.annotation.GuardedBy
-import androidx.annotation.IntDef
 import java.util.WeakHashMap
 import java.util.concurrent.Executor
 
-/** The reason of a change. */
-@IntDef(
-    ChangeReason.UNKNOWN,
-    ChangeReason.UPDATE,
-    ChangeReason.DELETE,
-    ChangeReason.RESTORE,
-    ChangeReason.SYNC_ACROSS_PROFILES,
-)
-@Retention(AnnotationRetention.SOURCE)
-annotation class ChangeReason {
-    companion object {
-        /** Unknown reason of the change. */
-        const val UNKNOWN = 0
-        /** Data is updated. */
-        const val UPDATE = 1
-        /** Data is deleted. */
-        const val DELETE = 2
-        /** Data is restored from backup/restore framework. */
-        const val RESTORE = 3
-        /** Data is synced from another profile (e.g. personal profile to work profile). */
-        const val SYNC_ACROSS_PROFILES = 4
-    }
-}
-
 /**
  * Callback to be informed of changes in [Observable] object.
  *
@@ -60,7 +35,7 @@
      * @param reason the reason of change
      * @see [Observable.addObserver] for the notices.
      */
-    fun onChanged(@ChangeReason reason: Int)
+    fun onChanged(reason: Int)
 }
 
 /** An observable object allows to observe change with [Observer]. */
@@ -90,7 +65,7 @@
      *
      * @param reason reason of the change
      */
-    fun notifyChange(@ChangeReason reason: Int)
+    fun notifyChange(reason: Int)
 }
 
 /** A thread safe implementation of [Observable]. */
@@ -110,7 +85,7 @@
         synchronized(observers) { observers.remove(observer) }
     }
 
-    override fun notifyChange(@ChangeReason reason: Int) {
+    override fun notifyChange(reason: Int) {
         // make a copy to avoid potential ConcurrentModificationException
         val entries = synchronized(observers) { observers.entries.toTypedArray() }
         for (entry in entries) {
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt
index 9f9c0d8..20a95d7 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt
@@ -83,10 +83,10 @@
     private val sharedPreferencesListener =
         SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
             if (key != null) {
-                notifyChange(key, ChangeReason.UPDATE)
+                notifyChange(key, DataChangeReason.UPDATE)
             } else {
                 // On Android >= R, SharedPreferences.Editor.clear() will trigger this case
-                notifyChange(ChangeReason.DELETE)
+                notifyChange(DataChangeReason.DELETE)
             }
         }
 
diff --git a/packages/SettingsLib/DataStore/tests/Android.bp b/packages/SettingsLib/DataStore/tests/Android.bp
index 5d000eb..2e3b42d 100644
--- a/packages/SettingsLib/DataStore/tests/Android.bp
+++ b/packages/SettingsLib/DataStore/tests/Android.bp
@@ -26,4 +26,5 @@
     instrumentation_for: "SettingsLibDataStoreShell",
     coverage_libs: ["SettingsLibDataStore"],
     upstream: true,
+    strict_mode: false,
 }
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt
index d8f5028..19c574a 100644
--- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt
@@ -157,9 +157,9 @@
 
         manager.onRestoreFinished()
 
-        verify(keyedObserver).onKeyChanged("key", ChangeReason.RESTORE)
-        verify(anyKeyObserver).onKeyChanged(null, ChangeReason.RESTORE)
-        verify(observer).onChanged(ChangeReason.RESTORE)
+        verify(keyedObserver).onKeyChanged("key", DataChangeReason.RESTORE)
+        verify(anyKeyObserver).onKeyChanged(null, DataChangeReason.RESTORE)
+        verify(observer).onChanged(DataChangeReason.RESTORE)
         if (isRobolectric()) {
             Shadows.shadowOf(BackupManager(application)).apply {
                 assertThat(isDataChanged).isFalse()
@@ -186,8 +186,8 @@
             assertThat(dataChangedCount).isEqualTo(0)
         }
 
-        fileStorage.notifyChange(ChangeReason.UPDATE)
-        verify(observer).onChanged(ChangeReason.UPDATE)
+        fileStorage.notifyChange(DataChangeReason.UPDATE)
+        verify(observer).onChanged(DataChangeReason.UPDATE)
         verify(keyedObserver, never()).onKeyChanged(any(), any())
         verify(anyKeyObserver, never()).onKeyChanged(any(), any())
         reset(observer)
@@ -196,10 +196,10 @@
             assertThat(dataChangedCount).isEqualTo(1)
         }
 
-        keyedStorage.notifyChange("key", ChangeReason.DELETE)
+        keyedStorage.notifyChange("key", DataChangeReason.DELETE)
         verify(observer, never()).onChanged(any())
-        verify(keyedObserver).onKeyChanged("key", ChangeReason.DELETE)
-        verify(anyKeyObserver).onKeyChanged("key", ChangeReason.DELETE)
+        verify(keyedObserver).onKeyChanged("key", DataChangeReason.DELETE)
+        verify(anyKeyObserver).onKeyChanged("key", DataChangeReason.DELETE)
         backupManager?.apply {
             assertThat(isDataChanged).isTrue()
             assertThat(dataChangedCount).isEqualTo(2)
@@ -207,11 +207,11 @@
         reset(keyedObserver)
 
         // backup manager is not notified for restore event
-        fileStorage.notifyChange(ChangeReason.RESTORE)
-        keyedStorage.notifyChange("key", ChangeReason.RESTORE)
-        verify(observer).onChanged(ChangeReason.RESTORE)
-        verify(keyedObserver).onKeyChanged("key", ChangeReason.RESTORE)
-        verify(anyKeyObserver).onKeyChanged("key", ChangeReason.RESTORE)
+        fileStorage.notifyChange(DataChangeReason.RESTORE)
+        keyedStorage.notifyChange("key", DataChangeReason.RESTORE)
+        verify(observer).onChanged(DataChangeReason.RESTORE)
+        verify(keyedObserver).onKeyChanged("key", DataChangeReason.RESTORE)
+        verify(anyKeyObserver).onKeyChanged("key", DataChangeReason.RESTORE)
         backupManager?.apply {
             assertThat(isDataChanged).isTrue()
             assertThat(dataChangedCount).isEqualTo(2)
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt
index 8638b2f..0fdecb0 100644
--- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt
@@ -77,7 +77,7 @@
         var observer: KeyedObserver<Any?>? = KeyedObserver { _, _ -> counter.incrementAndGet() }
         keyedObservable.addObserver(observer!!, executor1)
 
-        keyedObservable.notifyChange(ChangeReason.UPDATE)
+        keyedObservable.notifyChange(DataChangeReason.UPDATE)
         assertThat(counter.get()).isEqualTo(1)
 
         // trigger GC, the observer callback should not be invoked
@@ -85,7 +85,7 @@
         System.gc()
         System.runFinalization()
 
-        keyedObservable.notifyChange(ChangeReason.UPDATE)
+        keyedObservable.notifyChange(DataChangeReason.UPDATE)
         assertThat(counter.get()).isEqualTo(1)
     }
 
@@ -95,7 +95,7 @@
         var keyObserver: KeyedObserver<Any>? = KeyedObserver { _, _ -> counter.incrementAndGet() }
         keyedObservable.addObserver(key1, keyObserver!!, executor1)
 
-        keyedObservable.notifyChange(key1, ChangeReason.UPDATE)
+        keyedObservable.notifyChange(key1, DataChangeReason.UPDATE)
         assertThat(counter.get()).isEqualTo(1)
 
         // trigger GC, the observer callback should not be invoked
@@ -103,7 +103,7 @@
         System.gc()
         System.runFinalization()
 
-        keyedObservable.notifyChange(key1, ChangeReason.UPDATE)
+        keyedObservable.notifyChange(key1, DataChangeReason.UPDATE)
         assertThat(counter.get()).isEqualTo(1)
     }
 
@@ -112,16 +112,16 @@
         keyedObservable.addObserver(observer1, executor1)
         keyedObservable.addObserver(observer2, executor2)
 
-        keyedObservable.notifyChange(ChangeReason.UPDATE)
-        verify(observer1).onKeyChanged(null, ChangeReason.UPDATE)
-        verify(observer2).onKeyChanged(null, ChangeReason.UPDATE)
+        keyedObservable.notifyChange(DataChangeReason.UPDATE)
+        verify(observer1).onKeyChanged(null, DataChangeReason.UPDATE)
+        verify(observer2).onKeyChanged(null, DataChangeReason.UPDATE)
 
         reset(observer1, observer2)
         keyedObservable.removeObserver(observer2)
 
-        keyedObservable.notifyChange(ChangeReason.DELETE)
-        verify(observer1).onKeyChanged(null, ChangeReason.DELETE)
-        verify(observer2, never()).onKeyChanged(null, ChangeReason.DELETE)
+        keyedObservable.notifyChange(DataChangeReason.DELETE)
+        verify(observer1).onKeyChanged(null, DataChangeReason.DELETE)
+        verify(observer2, never()).onKeyChanged(null, DataChangeReason.DELETE)
     }
 
     @Test
@@ -129,16 +129,16 @@
         keyedObservable.addObserver(key1, keyedObserver1, executor1)
         keyedObservable.addObserver(key2, keyedObserver2, executor2)
 
-        keyedObservable.notifyChange(key1, ChangeReason.UPDATE)
-        verify(keyedObserver1).onKeyChanged(key1, ChangeReason.UPDATE)
-        verify(keyedObserver2, never()).onKeyChanged(key2, ChangeReason.UPDATE)
+        keyedObservable.notifyChange(key1, DataChangeReason.UPDATE)
+        verify(keyedObserver1).onKeyChanged(key1, DataChangeReason.UPDATE)
+        verify(keyedObserver2, never()).onKeyChanged(key2, DataChangeReason.UPDATE)
 
         reset(keyedObserver1, keyedObserver2)
         keyedObservable.removeObserver(key1, keyedObserver1)
 
-        keyedObservable.notifyChange(key1, ChangeReason.DELETE)
-        verify(keyedObserver1, never()).onKeyChanged(key1, ChangeReason.DELETE)
-        verify(keyedObserver2, never()).onKeyChanged(key2, ChangeReason.DELETE)
+        keyedObservable.notifyChange(key1, DataChangeReason.DELETE)
+        verify(keyedObserver1, never()).onKeyChanged(key1, DataChangeReason.DELETE)
+        verify(keyedObserver2, never()).onKeyChanged(key2, DataChangeReason.DELETE)
     }
 
     @Test
@@ -147,24 +147,24 @@
         keyedObservable.addObserver(key1, keyedObserver1, executor1)
         keyedObservable.addObserver(key2, keyedObserver2, executor1)
 
-        keyedObservable.notifyChange(ChangeReason.UPDATE)
-        verify(observer1).onKeyChanged(null, ChangeReason.UPDATE)
-        verify(keyedObserver1).onKeyChanged(key1, ChangeReason.UPDATE)
-        verify(keyedObserver2).onKeyChanged(key2, ChangeReason.UPDATE)
+        keyedObservable.notifyChange(DataChangeReason.UPDATE)
+        verify(observer1).onKeyChanged(null, DataChangeReason.UPDATE)
+        verify(keyedObserver1).onKeyChanged(key1, DataChangeReason.UPDATE)
+        verify(keyedObserver2).onKeyChanged(key2, DataChangeReason.UPDATE)
 
         reset(observer1, keyedObserver1, keyedObserver2)
-        keyedObservable.notifyChange(key1, ChangeReason.UPDATE)
+        keyedObservable.notifyChange(key1, DataChangeReason.UPDATE)
 
-        verify(observer1).onKeyChanged(key1, ChangeReason.UPDATE)
-        verify(keyedObserver1).onKeyChanged(key1, ChangeReason.UPDATE)
-        verify(keyedObserver2, never()).onKeyChanged(key1, ChangeReason.UPDATE)
+        verify(observer1).onKeyChanged(key1, DataChangeReason.UPDATE)
+        verify(keyedObserver1).onKeyChanged(key1, DataChangeReason.UPDATE)
+        verify(keyedObserver2, never()).onKeyChanged(key1, DataChangeReason.UPDATE)
 
         reset(observer1, keyedObserver1, keyedObserver2)
-        keyedObservable.notifyChange(key2, ChangeReason.UPDATE)
+        keyedObservable.notifyChange(key2, DataChangeReason.UPDATE)
 
-        verify(observer1).onKeyChanged(key2, ChangeReason.UPDATE)
-        verify(keyedObserver1, never()).onKeyChanged(key2, ChangeReason.UPDATE)
-        verify(keyedObserver2).onKeyChanged(key2, ChangeReason.UPDATE)
+        verify(observer1).onKeyChanged(key2, DataChangeReason.UPDATE)
+        verify(keyedObserver1, never()).onKeyChanged(key2, DataChangeReason.UPDATE)
+        verify(keyedObserver2).onKeyChanged(key2, DataChangeReason.UPDATE)
     }
 
     @Test
@@ -176,7 +176,7 @@
 
         keyedObservable.addObserver(observer, executor1)
 
-        keyedObservable.notifyChange(ChangeReason.UPDATE)
+        keyedObservable.notifyChange(DataChangeReason.UPDATE)
         keyedObservable.removeObserver(observer)
     }
 
@@ -189,7 +189,7 @@
 
         keyedObservable.addObserver(key1, keyObserver, executor1)
 
-        keyedObservable.notifyChange(key1, ChangeReason.UPDATE)
+        keyedObservable.notifyChange(key1, DataChangeReason.UPDATE)
         keyedObservable.removeObserver(key1, keyObserver)
     }
 }
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt
index 173c2b1..5d0303c 100644
--- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt
@@ -58,7 +58,7 @@
         var observer: Observer? = Observer { counter.incrementAndGet() }
         observable.addObserver(observer!!, executor1)
 
-        observable.notifyChange(ChangeReason.UPDATE)
+        observable.notifyChange(DataChangeReason.UPDATE)
         assertThat(counter.get()).isEqualTo(1)
 
         // trigger GC, the observer callback should not be invoked
@@ -66,7 +66,7 @@
         System.gc()
         System.runFinalization()
 
-        observable.notifyChange(ChangeReason.UPDATE)
+        observable.notifyChange(DataChangeReason.UPDATE)
         assertThat(counter.get()).isEqualTo(1)
     }
 
@@ -75,17 +75,17 @@
         observable.addObserver(observer1, executor1)
         observable.addObserver(observer2, executor2)
 
-        observable.notifyChange(ChangeReason.DELETE)
+        observable.notifyChange(DataChangeReason.DELETE)
 
-        verify(observer1).onChanged(ChangeReason.DELETE)
-        verify(observer2).onChanged(ChangeReason.DELETE)
+        verify(observer1).onChanged(DataChangeReason.DELETE)
+        verify(observer2).onChanged(DataChangeReason.DELETE)
 
         reset(observer1, observer2)
         observable.removeObserver(observer2)
 
-        observable.notifyChange(ChangeReason.UPDATE)
-        verify(observer1).onChanged(ChangeReason.UPDATE)
-        verify(observer2, never()).onChanged(ChangeReason.UPDATE)
+        observable.notifyChange(DataChangeReason.UPDATE)
+        verify(observer1).onChanged(DataChangeReason.UPDATE)
+        verify(observer2, never()).onChanged(DataChangeReason.UPDATE)
     }
 
     @Test
@@ -93,7 +93,7 @@
         // ConcurrentModificationException is raised if it is not implemented correctly
         val observer = Observer { observable.addObserver(observer1, executor1) }
         observable.addObserver(observer, executor1)
-        observable.notifyChange(ChangeReason.UPDATE)
+        observable.notifyChange(DataChangeReason.UPDATE)
         observable.removeObserver(observer)
     }
 }
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/SharedPreferencesStorageTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/SharedPreferencesStorageTest.kt
index fec7d75..a135d77 100644
--- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/SharedPreferencesStorageTest.kt
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/SharedPreferencesStorageTest.kt
@@ -80,13 +80,13 @@
         storage.addObserver("key", keyedObserver, executor)
 
         storage.sharedPreferences.edit().putString("key", "string").applySync()
-        verify(observer).onKeyChanged("key", ChangeReason.UPDATE)
-        verify(keyedObserver).onKeyChanged("key", ChangeReason.UPDATE)
+        verify(observer).onKeyChanged("key", DataChangeReason.UPDATE)
+        verify(keyedObserver).onKeyChanged("key", DataChangeReason.UPDATE)
 
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
             storage.sharedPreferences.edit().clear().applySync()
-            verify(observer).onKeyChanged(null, ChangeReason.DELETE)
-            verify(keyedObserver).onKeyChanged("key", ChangeReason.DELETE)
+            verify(observer).onKeyChanged(null, DataChangeReason.DELETE)
+            verify(keyedObserver).onKeyChanged("key", DataChangeReason.DELETE)
         }
     }
 
diff --git a/packages/SettingsLib/IllustrationPreference/Android.bp b/packages/SettingsLib/IllustrationPreference/Android.bp
index c3a91a2..cd8f584 100644
--- a/packages/SettingsLib/IllustrationPreference/Android.bp
+++ b/packages/SettingsLib/IllustrationPreference/Android.bp
@@ -47,6 +47,7 @@
     aconfig_declarations: "settingslib_illustrationpreference_flags",
 
     min_sdk_version: "30",
+    sdk_version: "system_current",
 
     apex_available: [
         "//apex_available:platform",
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 bc3488fc..cf645f7 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
@@ -41,6 +41,9 @@
     static {
         HashMap<String, Integer> map = new HashMap<>();
         map.put(
+                ".grey200",
+                R.color.settingslib_color_grey800);
+        map.put(
                 ".grey600",
                 R.color.settingslib_color_grey400);
         map.put(
@@ -56,9 +59,6 @@
                 ".black",
                 android.R.color.white);
         map.put(
-                ".blue200",
-                R.color.settingslib_color_blue700);
-        map.put(
                 ".blue400",
                 R.color.settingslib_color_blue600);
         map.put(
@@ -70,6 +70,9 @@
         map.put(
                 ".red200",
                 R.color.settingslib_color_red500);
+        map.put(
+                ".cream",
+                R.color.settingslib_color_charcoal);
         DARK_TO_LIGHT_THEME_COLOR_MAP = Collections.unmodifiableMap(map);
     }
 
diff --git a/packages/SettingsLib/ProfileSelector/res/values-cs/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-cs/strings.xml
index d50fc9a..3e5f526 100644
--- a/packages/SettingsLib/ProfileSelector/res/values-cs/strings.xml
+++ b/packages/SettingsLib/ProfileSelector/res/values-cs/strings.xml
@@ -18,6 +18,6 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="settingslib_category_personal" msgid="1142302328104700620">"Osobní"</string>
-    <string name="settingslib_category_work" msgid="4867750733682444676">"Prácovní"</string>
+    <string name="settingslib_category_work" msgid="4867750733682444676">"Pracovní"</string>
     <string name="settingslib_category_private" msgid="5039276873477591386">"Soukromé"</string>
 </resources>
diff --git a/packages/SettingsLib/ProfileSelector/res/values-el/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-el/strings.xml
index 628388d..b1bc69c 100644
--- a/packages/SettingsLib/ProfileSelector/res/values-el/strings.xml
+++ b/packages/SettingsLib/ProfileSelector/res/values-el/strings.xml
@@ -19,5 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="settingslib_category_personal" msgid="1142302328104700620">"Προσωπικά"</string>
     <string name="settingslib_category_work" msgid="4867750733682444676">"Εργασία"</string>
-    <string name="settingslib_category_private" msgid="5039276873477591386">"Ιδιωτικό"</string>
+    <string name="settingslib_category_private" msgid="5039276873477591386">"Ιδιωτικός"</string>
 </resources>
diff --git a/packages/SettingsLib/ProfileSelector/res/values-is/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-is/strings.xml
index 75668e8..b7aa61b 100644
--- a/packages/SettingsLib/ProfileSelector/res/values-is/strings.xml
+++ b/packages/SettingsLib/ProfileSelector/res/values-is/strings.xml
@@ -19,5 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="settingslib_category_personal" msgid="1142302328104700620">"Persónulegt"</string>
     <string name="settingslib_category_work" msgid="4867750733682444676">"Vinna"</string>
-    <string name="settingslib_category_private" msgid="5039276873477591386">"Lokað"</string>
+    <string name="settingslib_category_private" msgid="5039276873477591386">"Laynirými"</string>
 </resources>
diff --git a/packages/SettingsLib/ProfileSelector/res/values-ja/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-ja/strings.xml
index 21419e6..c9faa3c3 100644
--- a/packages/SettingsLib/ProfileSelector/res/values-ja/strings.xml
+++ b/packages/SettingsLib/ProfileSelector/res/values-ja/strings.xml
@@ -19,5 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="settingslib_category_personal" msgid="1142302328104700620">"個人用"</string>
     <string name="settingslib_category_work" msgid="4867750733682444676">"仕事用"</string>
-    <string name="settingslib_category_private" msgid="5039276873477591386">"非公開"</string>
+    <string name="settingslib_category_private" msgid="5039276873477591386">"プライベート"</string>
 </resources>
diff --git a/packages/SettingsLib/ProfileSelector/res/values-mk/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-mk/strings.xml
index 07cf9c7..e8c2bf5 100644
--- a/packages/SettingsLib/ProfileSelector/res/values-mk/strings.xml
+++ b/packages/SettingsLib/ProfileSelector/res/values-mk/strings.xml
@@ -19,5 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="settingslib_category_personal" msgid="1142302328104700620">"Лични"</string>
     <string name="settingslib_category_work" msgid="4867750733682444676">"Работа"</string>
-    <string name="settingslib_category_private" msgid="5039276873477591386">"Приватен"</string>
+    <string name="settingslib_category_private" msgid="5039276873477591386">"Приватно"</string>
 </resources>
diff --git a/packages/SettingsLib/ProfileSelector/res/values-pa/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-pa/strings.xml
index e1e68c7..1e4abc1 100644
--- a/packages/SettingsLib/ProfileSelector/res/values-pa/strings.xml
+++ b/packages/SettingsLib/ProfileSelector/res/values-pa/strings.xml
@@ -18,6 +18,6 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="settingslib_category_personal" msgid="1142302328104700620">"ਨਿੱਜੀ"</string>
-    <string name="settingslib_category_work" msgid="4867750733682444676">"ਕਾਰਜ"</string>
+    <string name="settingslib_category_work" msgid="4867750733682444676">"ਕੰਮ ਸੰਬੰਧੀ"</string>
     <string name="settingslib_category_private" msgid="5039276873477591386">"ਪ੍ਰਾਈਵੇਟ"</string>
 </resources>
diff --git a/packages/SettingsLib/ProfileSelector/res/values-ru/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-ru/strings.xml
index ee4212f..e0b1471 100644
--- a/packages/SettingsLib/ProfileSelector/res/values-ru/strings.xml
+++ b/packages/SettingsLib/ProfileSelector/res/values-ru/strings.xml
@@ -19,5 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="settingslib_category_personal" msgid="1142302328104700620">"Личный профиль"</string>
     <string name="settingslib_category_work" msgid="4867750733682444676">"Рабочий профиль"</string>
-    <string name="settingslib_category_private" msgid="5039276873477591386">"Личное"</string>
+    <string name="settingslib_category_private" msgid="5039276873477591386">"Частный профиль"</string>
 </resources>
diff --git a/packages/SettingsLib/ProfileSelector/res/values-tr/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-tr/strings.xml
index 59f21c8..e4e2bdc 100644
--- a/packages/SettingsLib/ProfileSelector/res/values-tr/strings.xml
+++ b/packages/SettingsLib/ProfileSelector/res/values-tr/strings.xml
@@ -19,5 +19,5 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="settingslib_category_personal" msgid="1142302328104700620">"Kişisel"</string>
     <string name="settingslib_category_work" msgid="4867750733682444676">"İş"</string>
-    <string name="settingslib_category_private" msgid="5039276873477591386">"Gizli"</string>
+    <string name="settingslib_category_private" msgid="5039276873477591386">"Özel"</string>
 </resources>
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/Android.bp b/packages/SettingsLib/Spa/screenshot/robotests/Android.bp
index 6b8197c..c834c80 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/Android.bp
+++ b/packages/SettingsLib/Spa/screenshot/robotests/Android.bp
@@ -71,4 +71,6 @@
     upstream: true,
     java_resource_dirs: ["config"],
     instrumentation_for: "SpaRoboApp",
+
+    strict_mode: false,
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/LifecycleEffect.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/LifecycleEffect.kt
index e91fa65..e9f9689 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/LifecycleEffect.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/LifecycleEffect.kt
@@ -18,9 +18,9 @@
 
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
-import androidx.compose.ui.platform.LocalLifecycleOwner
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.LifecycleEventObserver
+import androidx.lifecycle.compose.LocalLifecycleOwner
 
 @Composable
 fun LifecycleEffect(
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/OnBackEffect.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/OnBackEffect.kt
index 3991f26..0b1c92d 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/OnBackEffect.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/OnBackEffect.kt
@@ -24,7 +24,7 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberUpdatedState
-import androidx.compose.ui.platform.LocalLifecycleOwner
+import androidx.lifecycle.compose.LocalLifecycleOwner
 
 /**
  * An effect for detecting presses of the system back button, and the back event will not be
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/presenter/Demo.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/presenter/Demo.kt
index ee24a09..007f47b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/presenter/Demo.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/slice/presenter/Demo.kt
@@ -21,8 +21,8 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalLifecycleOwner
 import androidx.compose.ui.viewinterop.AndroidView
+import androidx.lifecycle.compose.LocalLifecycleOwner
 import androidx.slice.widget.SliceLiveData
 import androidx.slice.widget.SliceView
 
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialog.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialog.kt
index de080e3..022dded 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialog.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialog.kt
@@ -38,6 +38,7 @@
 
 data class AlertDialogButton(
     val text: String,
+    val enabled: Boolean = true,
     val onClick: () -> Unit = {},
 )
 
@@ -114,6 +115,7 @@
             close()
             button.onClick()
         },
+        enabled = button.enabled,
     ) {
         Text(button.text)
     }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/DropdownTextBox.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/DropdownTextBox.kt
index b471e50..bdbe62c 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/DropdownTextBox.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/editor/DropdownTextBox.kt
@@ -22,6 +22,7 @@
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.ExposedDropdownMenuBox
 import androidx.compose.material3.ExposedDropdownMenuDefaults
+import androidx.compose.material3.MenuAnchorType
 import androidx.compose.material3.OutlinedTextField
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
@@ -65,7 +66,7 @@
         OutlinedTextField(
             // The `menuAnchor` modifier must be passed to the text field for correctness.
             modifier = Modifier
-                .menuAnchor()
+                .menuAnchor(MenuAnchorType.PrimaryNotEditable)
                 .fillMaxWidth(),
             value = text,
             onValueChange = { },
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt
index a6cc3a9..ea4480f 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Lottie.kt
@@ -49,6 +49,7 @@
 
 object LottieColorUtils {
     private val DARK_TO_LIGHT_THEME_COLOR_MAP = mapOf(
+        ".grey200" to R.color.settingslib_color_grey800,
         ".grey600" to R.color.settingslib_color_grey400,
         ".grey800" to R.color.settingslib_color_grey300,
         ".grey900" to R.color.settingslib_color_grey50,
@@ -58,6 +59,7 @@
         ".green400" to R.color.settingslib_color_green600,
         ".green200" to R.color.settingslib_color_green500,
         ".red200" to R.color.settingslib_color_red500,
+        ".cream" to R.color.settingslib_color_charcoal
     )
 
     @Composable
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/LifecycleEffectTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/LifecycleEffectTest.kt
index fe7baff..8b0efff 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/LifecycleEffectTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/compose/LifecycleEffectTest.kt
@@ -18,9 +18,9 @@
 
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.ui.platform.LocalLifecycleOwner
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.compose.LocalLifecycleOwner
 import androidx.lifecycle.testing.TestLifecycleOwner
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.google.common.truth.Truth.assertThat
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialogTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialogTest.kt
index 9468f95..20ea397 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialogTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/dialog/SettingsAlertDialogTest.kt
@@ -20,6 +20,8 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.assertIsEnabled
+import androidx.compose.ui.test.assertIsNotEnabled
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.performClick
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -67,7 +69,18 @@
             rememberAlertDialogPresenter(confirmButton = AlertDialogButton(CONFIRM_TEXT))
         }
 
-        composeTestRule.onDialogText(CONFIRM_TEXT).assertIsDisplayed()
+        composeTestRule.onDialogText(CONFIRM_TEXT).assertIsDisplayed().assertIsEnabled()
+    }
+
+    @Test
+    fun confirmButton_disabled() {
+        setAndOpenDialog {
+            rememberAlertDialogPresenter(
+                confirmButton = AlertDialogButton(text = CONFIRM_TEXT, enabled = false)
+            )
+        }
+
+        composeTestRule.onDialogText(CONFIRM_TEXT).assertIsDisplayed().assertIsNotEnabled()
     }
 
     @Test
@@ -90,7 +103,18 @@
             rememberAlertDialogPresenter(dismissButton = AlertDialogButton(DISMISS_TEXT))
         }
 
-        composeTestRule.onDialogText(DISMISS_TEXT).assertIsDisplayed()
+        composeTestRule.onDialogText(DISMISS_TEXT).assertIsDisplayed().assertIsEnabled()
+    }
+
+    @Test
+    fun dismissButton_disabled() {
+        setAndOpenDialog {
+            rememberAlertDialogPresenter(
+                dismissButton = AlertDialogButton(text = DISMISS_TEXT, enabled = false)
+            )
+        }
+
+        composeTestRule.onDialogText(DISMISS_TEXT).assertIsDisplayed().assertIsNotEnabled()
     }
 
     @Test
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CopyableBodyTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CopyableBodyTest.kt
index 71072a5..d91c7e6 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CopyableBodyTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CopyableBodyTest.kt
@@ -16,9 +16,9 @@
 
 package com.android.settingslib.spa.widget.ui
 
-import android.content.ClipData
-import android.content.ClipboardManager
 import android.content.Context
+import androidx.compose.ui.platform.ClipboardManager
+import androidx.compose.ui.platform.LocalClipboardManager
 import androidx.compose.ui.test.assertIsDisplayed
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.longClick
@@ -63,9 +63,9 @@
 
     @Test
     fun onCopy_saveToClipboard() {
-        val clipboardManager = context.getSystemService(ClipboardManager::class.java)!!
-        clipboardManager.setPrimaryClip(ClipData.newPlainText("", ""))
+        var clipboardManager: ClipboardManager? = null
         composeTestRule.setContent {
+            clipboardManager = LocalClipboardManager.current
             CopyableBody(TEXT)
         }
 
@@ -74,7 +74,7 @@
         }
         composeTestRule.onNodeWithText(context.getString(android.R.string.copy)).performClick()
 
-        assertThat(clipboardManager.primaryClip!!.getItemAt(0).text.toString()).isEqualTo(TEXT)
+        assertThat(clipboardManager?.getText()?.text).isEqualTo(TEXT)
     }
 
     private companion object {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt
index 977615b..f95cfc3 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItem.kt
@@ -30,22 +30,24 @@
 @Composable
 fun MoreOptionsScope.RestrictedMenuItem(
     text: String,
+    enabled: Boolean = true,
     restrictions: Restrictions,
     onClick: () -> Unit,
 ) {
-    RestrictedMenuItemImpl(text, restrictions, onClick, ::RestrictionsProviderImpl)
+    RestrictedMenuItemImpl(text, enabled, restrictions, onClick, ::RestrictionsProviderImpl)
 }
 
 @VisibleForTesting
 @Composable
 internal fun MoreOptionsScope.RestrictedMenuItemImpl(
     text: String,
+    enabled: Boolean = true,
     restrictions: Restrictions,
     onClick: () -> Unit,
     restrictionsProviderFactory: RestrictionsProviderFactory,
 ) {
     val restrictedMode = restrictionsProviderFactory.rememberRestrictedMode(restrictions).value
-    MenuItem(text = text, enabled = restrictedMode !== BaseUserRestricted) {
+    MenuItem(text = text, enabled = enabled && restrictedMode !== BaseUserRestricted) {
         when (restrictedMode) {
             is BlockedByAdmin -> restrictedMode.sendShowAdminSupportDetailsIntent()
             is BlockedByEcm -> restrictedMode.showRestrictedSettingsDetails()
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItemTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItemTest.kt
index 556adc7..4068bce 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItemTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/scaffold/RestrictedMenuItemTest.kt
@@ -49,6 +49,15 @@
     private var menuItemOnClickIsCalled = false
 
     @Test
+    fun whenDisabled() {
+        val restrictions = Restrictions(userId = USER_ID, keys = emptyList())
+
+        setContent(restrictions, enabled = false)
+
+        composeTestRule.onNodeWithText(TEXT).assertIsDisplayed().assertIsNotEnabled()
+    }
+
+    @Test
     fun whenRestrictionsKeysIsEmpty_enabled() {
         val restrictions = Restrictions(userId = USER_ID, keys = emptyList())
 
@@ -153,13 +162,14 @@
         assertThat(menuItemOnClickIsCalled).isFalse()
     }
 
-    private fun setContent(restrictions: Restrictions) {
+    private fun setContent(restrictions: Restrictions, enabled: Boolean = true) {
         val fakeMoreOptionsScope = object : MoreOptionsScope() {
             override fun dismiss() {}
         }
         composeTestRule.setContent {
             fakeMoreOptionsScope.RestrictedMenuItemImpl(
                 text = TEXT,
+                enabled = enabled,
                 restrictions = restrictions,
                 onClick = { menuItemOnClickIsCalled = true },
                 restrictionsProviderFactory = { _, _ -> fakeRestrictionsProvider },
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 5e80604..6b6f803 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> battery."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Links: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Regs: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Links <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Regs <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktief"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Gestoor"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktief (net links)"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index eb1a365..6f494f0 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"ግ፦ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>፣ ቀ፦ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ባትሪ።"</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"ግራ፦ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ባትሪ"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"ቀኝ፦ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ባትሪ"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"ግራ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"ቀኝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"ንቁ"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"ተቀምጧል"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"ገቢር (ግራ ብቻ)"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index e64a742..d021647 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -237,7 +237,7 @@
     <string name="choose_profile" msgid="343803890897657450">"প্ৰ’ফাইল বাছনি কৰক"</string>
     <string name="category_personal" msgid="6236798763159385225">"ব্যক্তিগত"</string>
     <string name="category_work" msgid="4014193632325996115">"কৰ্মস্থান-সম্পৰ্কীয়"</string>
-    <string name="category_private" msgid="4244892185452788977">"ব্যক্তিগত"</string>
+    <string name="category_private" msgid="4244892185452788977">"প্ৰাইভেট"</string>
     <string name="category_clone" msgid="1554511758987195974">"ক্ল’ন"</string>
     <string name="development_settings_title" msgid="140296922921597393">"বিকাশকৰ্তাৰ বিকল্পসমূহ"</string>
     <string name="development_settings_enable" msgid="4285094651288242183">"বিকাশকৰ্তা বিষয়ক বিকল্পসমূহ সক্ষম কৰক"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 23bfcb5..b7dda83 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Sol: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Sağ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batareya."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Sol: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batareya"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Sağ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batareya"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Sol <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Sağ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktiv"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Yadda saxlandı"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktiv (yalnız sol)"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 71b7f6e..0e42066 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Levo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, desno: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> baterije."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Levo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterije"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Desno: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterije"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Levo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Desno: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktivan"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Sačuvano"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktivno (samo levo)"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 61ed0a1..a723223 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Зарад акумулятара: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> (левы навушнік), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> (правы навушнік)."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Зарад акумулятара: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> (левы навушнік)"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Зарад акумулятара: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> (правы навушнік)"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Левы: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Правы: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Уключана"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Захавана"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Выкарыстоўваецца (толькі левы навушнік)"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 0515ed9..54fef5f 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Л: батерия – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Д: батерия – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"За ляво ухо. Батерия: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"За дясно ухо. Батерия: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Вляво: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Вдясно: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Активно"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Запазено"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Активно (само лявото)"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index fea8137..1533a19 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"বাঁদিক: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, ডানদিক: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ব্যাটারি।"</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"বাঁদিক: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ব্যাটারি"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"ডানদিক: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ব্যাটারি"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"বাঁদিকের হেডসেটে <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> চার্জ আছে"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"ডানদিকের হেডসেটে <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> চার্জ আছে"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"চালু আছে"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"সেভ করা আছে"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"চালু আছে (শুধু বাঁদিক)"</string>
@@ -239,7 +237,7 @@
     <string name="choose_profile" msgid="343803890897657450">"প্রোফাইল বেছে নিন"</string>
     <string name="category_personal" msgid="6236798763159385225">"ব্যক্তিগত"</string>
     <string name="category_work" msgid="4014193632325996115">"অফিস"</string>
-    <string name="category_private" msgid="4244892185452788977">"ব্যক্তিগত"</string>
+    <string name="category_private" msgid="4244892185452788977">"প্রাইভেট"</string>
     <string name="category_clone" msgid="1554511758987195974">"ক্লোন"</string>
     <string name="development_settings_title" msgid="140296922921597393">"ডেভেলপার বিকল্প"</string>
     <string name="development_settings_enable" msgid="4285094651288242183">"ডেভেলপার বিকল্প সক্ষম করুন"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 7dce81a..cc67420 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"E: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de bateria, D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de bateria."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Esquerre: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Dret: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Esquerre: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Dret: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Actiu"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Desat"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Actiu (només l\'esquerre)"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 20ff455..194c616 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -237,7 +237,7 @@
     <string name="choose_profile" msgid="343803890897657450">"Vyberte profil"</string>
     <string name="category_personal" msgid="6236798763159385225">"Osobní"</string>
     <string name="category_work" msgid="4014193632325996115">"Pracovní"</string>
-    <string name="category_private" msgid="4244892185452788977">"Soukromé"</string>
+    <string name="category_private" msgid="4244892185452788977">"Soukromý"</string>
     <string name="category_clone" msgid="1554511758987195974">"Klon"</string>
     <string name="development_settings_title" msgid="140296922921597393">"Pro vývojáře"</string>
     <string name="development_settings_enable" msgid="4285094651288242183">"Aktivovat možnosti pro vývojáře"</string>
@@ -287,7 +287,7 @@
     <string name="oem_unlock_enable_summary" msgid="5857388174390953829">"Povolit odemknutí zavaděče"</string>
     <string name="confirm_enable_oem_unlock_title" msgid="8249318129774367535">"Povolit odemknutí OEM?"</string>
     <string name="confirm_enable_oem_unlock_text" msgid="854131050791011970">"UPOZORNĚNÍ: Pokud bude toto nastavení zapnuto, nebudou v tomto zařízení fungovat funkce ochrany zařízení."</string>
-    <string name="mock_location_app" msgid="6269380172542248304">"Vybrat aplikaci k simulování polohy"</string>
+    <string name="mock_location_app" msgid="6269380172542248304">"Vybrat aplikaci k simulování polohy"</string>
     <string name="mock_location_app_not_set" msgid="6972032787262831155">"Aplikace k simulování polohy není nastavena"</string>
     <string name="mock_location_app_set" msgid="4706722469342913843">"Aplikace k simulování polohy: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="debug_networking_category" msgid="6829757985772659599">"Sítě"</string>
@@ -602,7 +602,7 @@
     <string name="shared_data_no_blobs_text" msgid="3108114670341737434">"Pro tohoto uživatele nejsou k dispozici žádná sdílená data."</string>
     <string name="shared_data_query_failure_text" msgid="3489828881998773687">"Při načítání sdílených dat došlo k chybě. Zkuste to znovu."</string>
     <string name="blob_id_text" msgid="8680078988996308061">"ID sdílených dat: <xliff:g id="BLOB_ID">%d</xliff:g>"</string>
-    <string name="blob_expires_text" msgid="7882727111491739331">"Platnost vyprší <xliff:g id="DATE">%s</xliff:g>"</string>
+    <string name="blob_expires_text" msgid="7882727111491739331">"Platnost skončí <xliff:g id="DATE">%s</xliff:g>"</string>
     <string name="shared_data_delete_failure_text" msgid="3842701391009628947">"Při mazání sdílených dat došlo k chybě."</string>
     <string name="shared_data_no_accessors_dialog_text" msgid="8903738462570715315">"Pro tato sdílená data nejsou k dispozici žádné smlouvy. Chcete je smazat?"</string>
     <string name="accessor_info_title" msgid="8289823651512477787">"Aplikace, které sdílejí data"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index cfa64c0..f0fb4df 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Akku links: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Akku rechts: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Akku links: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Akku rechts: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Links – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Rechts – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktiv"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Gespeichert"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktiv (nur links)"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index bd1b848..94d4297 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Α: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Δ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> μπαταρία."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Αριστερά: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> μπαταρία"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Δεξιά: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> μπαταρία"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Αριστερό <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Δεξί <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Ενεργό"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Αποθηκεύτηκε"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Ενεργό (μόνο το αριστερό)"</string>
@@ -237,9 +235,9 @@
     <item msgid="6946761421234586000">"400%"</item>
   </string-array>
     <string name="choose_profile" msgid="343803890897657450">"Επιλογή προφίλ"</string>
-    <string name="category_personal" msgid="6236798763159385225">"Προσωπικό"</string>
+    <string name="category_personal" msgid="6236798763159385225">"Προσωπικός"</string>
     <string name="category_work" msgid="4014193632325996115">"Εργασίας"</string>
-    <string name="category_private" msgid="4244892185452788977">"Ιδιωτικό"</string>
+    <string name="category_private" msgid="4244892185452788977">"Ιδιωτικός"</string>
     <string name="category_clone" msgid="1554511758987195974">"Κλωνοποίηση"</string>
     <string name="development_settings_title" msgid="140296922921597393">"Επιλογές για προγραμματιστές"</string>
     <string name="development_settings_enable" msgid="4285094651288242183">"Ενεργοποίηση επιλογών για προγραμματιστές"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 0167ec1..b96dd33 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"I: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>; D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de batería."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Izquierdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Derecho: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Izquierdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Derecho: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Activo"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Guardado"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Activo (solo izquierdo)"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 0899d5a..3c63119 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Izquierdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de batería. Derecho: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de batería."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Izquierdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Derecho: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Izquierda <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Derecha <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Activo"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Guardado"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Activo (solo izquierdo)"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 6be5f6c..25f002a2 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"L aldearen bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>. R aldearen bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Ezkerreko aldearen bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Eskuineko aldearen bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Ezkerrekoa: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Eskuinekoa: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktibo"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Gordeta"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktibo (ezkerrekoa soilik)"</string>
@@ -239,7 +237,7 @@
     <string name="choose_profile" msgid="343803890897657450">"Aukeratu profila"</string>
     <string name="category_personal" msgid="6236798763159385225">"Pertsonalak"</string>
     <string name="category_work" msgid="4014193632325996115">"Lanekoak"</string>
-    <string name="category_private" msgid="4244892185452788977">"Pribatua"</string>
+    <string name="category_private" msgid="4244892185452788977">"Pribatuak"</string>
     <string name="category_clone" msgid="1554511758987195974">"Klonatu"</string>
     <string name="development_settings_title" msgid="140296922921597393">"Garatzaileentzako aukerak"</string>
     <string name="development_settings_enable" msgid="4285094651288242183">"Gaitu garatzaileen aukerak"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index a5fe65d..399e396 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"باتری چپ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>، باتری راست: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"باتری چپ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"باتری راست: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"چپ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"راست <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"فعال"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"ذخیره‌شده"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"فعال (فقط چپ)"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 03a6d43..d2afaac 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, O: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> virtaa."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Vasen: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> virtaa"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Oikea: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> virtaa."</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Vasen <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Oikea <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktiivinen"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Tallennettu"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktiivinen (vain vasen)"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 7d2bea0..5f4480c 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Gauche : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de batterie, droit : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de batterie."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Gauche : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batterie"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Droit : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batterie"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Gauche (<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>)"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Droit (<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>)"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Actif"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Enregistré"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Actif (gauche uniquement)"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index cc03004..874a153 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -492,7 +492,7 @@
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> (<xliff:g id="TIME">%2$s</xliff:g> para completar a carga)"</string>
     <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> (carga optimizada)"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> (cargando)"</string>
-    <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Completa á/s <xliff:g id="TIME">%3$s</xliff:g>"</string>
+    <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Completarase á/s <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Carga completa á/s <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Carga completa á/s <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"Completa á/s <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index a5bf093..07eda9a 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"ડાબી બાજુ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, જમણી બાજુ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> બૅટરી."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"ડાબી બાજુ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> બૅટરી"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"જમણી બાજુ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> બૅટરી"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"ડાબી બાજુ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> બૅટરી"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"જમણી બાજુ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> બૅટરી"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"સક્રિય"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"સાચવેલું"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"ચાલુ છે (માત્ર ડાબી બાજુ)"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 2aee1bc..f66499f 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -237,7 +237,7 @@
     <string name="choose_profile" msgid="343803890897657450">"प्रोफ़ाइल चुनें"</string>
     <string name="category_personal" msgid="6236798763159385225">"निजी"</string>
     <string name="category_work" msgid="4014193632325996115">"वर्क"</string>
-    <string name="category_private" msgid="4244892185452788977">"निजी"</string>
+    <string name="category_private" msgid="4244892185452788977">"प्राइवेट"</string>
     <string name="category_clone" msgid="1554511758987195974">"क्लोन"</string>
     <string name="development_settings_title" msgid="140296922921597393">"डेवलपर के लिए सेटिंग और टूल"</string>
     <string name="development_settings_enable" msgid="4285094651288242183">"डेवलपर के लिए सेटिंग और टूल चालू करें"</string>
@@ -708,7 +708,7 @@
     <string name="physical_keyboard_title" msgid="4811935435315835220">"फ़िज़िकल कीबोर्ड"</string>
     <string name="keyboard_layout_dialog_title" msgid="3927180147005616290">"कीबोर्ड का लेआउट चुनें"</string>
     <string name="keyboard_layout_default_label" msgid="1997292217218546957">"डिफ़ॉल्ट"</string>
-    <string name="turn_screen_on_title" msgid="2662312432042116026">"इन ऐप के पास स्क्रीन को चालू करने का कंट्रोल है"</string>
+    <string name="turn_screen_on_title" msgid="2662312432042116026">"इस ऐप के पास स्क्रीन को चालू करने का कंट्रोल है"</string>
     <string name="allow_turn_screen_on" msgid="6194845766392742639">"स्क्रीन चालू करने की अनुमति दें"</string>
     <string name="allow_turn_screen_on_description" msgid="43834403291575164">"ऐप्लिकेशन को स्क्रीन चालू करने की अनुमति दें. ऐसा करने पर, ऐप्लिकेशन आपकी अनुमति लिए बिना भी, जब चाहे स्क्रीन चालू कर सकता है."</string>
     <string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"<xliff:g id="APP_NAME">%1$s</xliff:g> पर ब्रॉडकास्ट करना रोकें?"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 0271b09..8ce90ec 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> baterije."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Lijeva strana: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterije"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Desna strana: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterije"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Lijevo <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Desno <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktivan"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Spremljeno"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktivno (samo lijevo)"</string>
@@ -494,7 +492,7 @@
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do napunjenosti"</string>
     <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje se optimizira"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – punjenje"</string>
-    <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – napunjeno do <xliff:g id="TIME">%3$s</xliff:g>"</string>
+    <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – bit će pun do <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – potpuno napunjeno do <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Potpuno napunjeno do <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"Napunjeno do <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 5c06624..fb142f6 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Akkumulátorok töltöttségi szintje: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> (bal) és <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> (jobb)."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Akkumulátor töltöttségi szintje: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> (bal)."</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Akkumulátor töltöttségi szintje: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> (jobb)."</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Bal: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Jobb: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktív"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Mentve"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktív (csak bal)"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 805284e..e7cbf2d 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Ձախ ականջակալի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, աջ ականջակալի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>։"</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Ձախ ականջակալի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Աջ ականջակալի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Ձախը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Աջը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Ակտիվ է"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Պահված է"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Ակտիվ է (միայն ձախ)"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 6b03ae5..82a390e 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Baterai L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Kiri: Baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Kanan: Baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Kiri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Kanan <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktif"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Disimpan"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktif (hanya kiri)"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index d89da77..47cdcca 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, H: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> rafhlöðuhleðsla."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Vinstri: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> rafhlöðuhleðsla"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Hægri: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> rafhlöðuhleðsla"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Vinstri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Hægri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Virkt"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Vistað"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Kveikt (eingöngu vinstra)"</string>
@@ -237,7 +235,7 @@
     <item msgid="6946761421234586000">"400%"</item>
   </string-array>
     <string name="choose_profile" msgid="343803890897657450">"Veldu snið"</string>
-    <string name="category_personal" msgid="6236798763159385225">"Persónulegt"</string>
+    <string name="category_personal" msgid="6236798763159385225">"Einkasnið"</string>
     <string name="category_work" msgid="4014193632325996115">"Vinna"</string>
     <string name="category_private" msgid="4244892185452788977">"Lokað"</string>
     <string name="category_clone" msgid="1554511758987195974">"Afrit"</string>
@@ -497,7 +495,7 @@
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Fullt kl. <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Fullhlaðið kl. <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Fullhlaðið kl. <xliff:g id="TIME">%1$s</xliff:g>"</string>
-    <string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"Fullt kl. <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"Fullhlaðin kl. <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Óþekkt"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Í hleðslu"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hröð hleðsla"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 92f9554..3714593 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"S: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> di batteria. D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> di batteria."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Sinistro: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> di batteria"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Destro: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> di batteria"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Sinistra: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Destra: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Attivo"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Dispositivo salvato"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Attivo (solo sinistro)"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 5eb9170..c2f4a8f 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"סוללה בצד שמאל: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, סוללה בצד ימין: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"סוללה בצד שמאל: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"סוללה בצד ימין: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"אוזנייה שמאלית: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"אוזנייה ימנית: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"פעיל"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"בוצעה שמירה"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"פעיל (שמאל בלבד)"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 0efe609..a1ea070 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Сол жақ: батарея зарядының деңгейі – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>. Оң жақ: батарея зарядының деңгейі — <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Сол жақ: батарея зарядының деңгейі – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Оң жақ: батарея зарядының деңгейі – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Сол: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Оң: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Қосулы"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Сақталған"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Істеп тұр (тек сол жағы)"</string>
@@ -494,7 +492,7 @@
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g>: толық зарядталуға <xliff:g id="TIME">%2$s</xliff:g> қалды"</string>
     <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядтау оңтайландырылды"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Зарядталып жатыр"</string>
-    <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Заряд толуына қалған уақыт: <xliff:g id="TIME">%3$s</xliff:g>"</string>
+    <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Зарядталып болады: <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Толық заряд алуға қалған уақыт: <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Толық заряд алуға қалған уақыт: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"Заряд толуына қалған уақыт: <xliff:g id="TIME">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 0882327..431bdf7 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"ឆ្វេង៖ ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> ស្ដាំ៖ ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>។"</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"ឆ្វេង៖ ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"ស្ដាំ៖ ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"ឆ្វេង <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"ស្ដាំ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"សកម្ម"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"បាន​រក្សាទុក"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"សកម្ម (ខាងឆ្វេងប៉ុណ្ណោះ)"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index d47c76b..1381903 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"ಎಡ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, ಬಲ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ಬ್ಯಾಟರಿ ಮಟ್ಟ."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"ಎಡ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ಬ್ಯಾಟರಿ ಮಟ್ಟ"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"ಬಲ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ಬ್ಯಾಟರಿ ಮಟ್ಟ"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"ಎಡ ಭಾಗದ ಬ್ಯಾಟರಿ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"ಬಲ ಭಾಗದ ಬ್ಯಾಟರಿ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"ಸಕ್ರಿಯ"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"ಸೇವ್ ಮಾಡಲಾಗಿದೆ"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"ಸಕ್ರಿಯವಾಗಿದೆ (ಎಡಕಿವಿಯ ಸಾಧನ ಮಾತ್ರ)"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 51c8475..9cb770f 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"배터리는 왼쪽 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, 오른쪽 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>입니다."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"왼쪽 배터리는 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>입니다."</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"오른쪽 배터리는 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>입니다."</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"왼쪽 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"오른쪽 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"활성"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"저장됨"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"활성(왼쪽만)"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 0560a81..64180a9 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Батарея: L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Сол кулак – батареянын деңгээли: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Оң кулак – батареянын деңгээли: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Сол: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Оң: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Жигердүү"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Сакталган"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Иштеп жатат (сол тарап гана)"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 201c7a0..a8c3f5a 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Akumulatora uzlādes līmenis kreisajā austiņā: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, labajā austiņā: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Akumulatora uzlādes līmenis kreisajā austiņā: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Akumulatora uzlādes līmenis labajā austiņā: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Kreisā: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Labā: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktīvs"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Saglabāta"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Ierīce aktīva (tikai kreisā auss)"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index a72a72e..b2456d3 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Л: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> батерија, Д: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> батерија."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Лево: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> батерија"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Десно: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> батерија"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Одлево: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Оддесно: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Активен"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Зачувано"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Активно (само лево)"</string>
@@ -237,9 +235,9 @@
     <item msgid="6946761421234586000">"400 %"</item>
   </string-array>
     <string name="choose_profile" msgid="343803890897657450">"Изберете профил"</string>
-    <string name="category_personal" msgid="6236798763159385225">"Личен"</string>
+    <string name="category_personal" msgid="6236798763159385225">"Лично"</string>
     <string name="category_work" msgid="4014193632325996115">"Работа"</string>
-    <string name="category_private" msgid="4244892185452788977">"Приватен"</string>
+    <string name="category_private" msgid="4244892185452788977">"Приватно"</string>
     <string name="category_clone" msgid="1554511758987195974">"Клон"</string>
     <string name="development_settings_title" msgid="140296922921597393">"Програмерски опции"</string>
     <string name="development_settings_enable" msgid="4285094651288242183">"Овозможете ги програмерските опции"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 41afe435..436326e 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"З: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Б: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> батарей."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Зүүн: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> батарей"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Баруун: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> батарей."</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Зүүн тал <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Баруун тал <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Идэвхтэй"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Хадгалсан"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Идэвхтэй (зөвхөн зүүн тал)"</string>
@@ -239,7 +237,7 @@
     <string name="choose_profile" msgid="343803890897657450">"Профайл сонгох"</string>
     <string name="category_personal" msgid="6236798763159385225">"Хувийн"</string>
     <string name="category_work" msgid="4014193632325996115">"Ажил"</string>
-    <string name="category_private" msgid="4244892185452788977">"Хувийн"</string>
+    <string name="category_private" msgid="4244892185452788977">"Хаалттай"</string>
     <string name="category_clone" msgid="1554511758987195974">"Клон"</string>
     <string name="development_settings_title" msgid="140296922921597393">"Хөгжүүлэгчийн тохиргоо"</string>
     <string name="development_settings_enable" msgid="4285094651288242183">"Хөгжүүлэгчийн сонголтыг идэвхжүүлэх"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index bd1659c..1a672c39 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, H: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batteri."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Venstre: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Høyre: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Venstre: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Høyre: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktiv"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Lagret"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktiv (bare venstre)"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 8fc58d1..562a558 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"ବାମ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, ଡାହାଣ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ବେଟେରୀ।"</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"ବାମ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ବେଟେରୀ"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"ଡାହାଣ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ବେଟେରୀ"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"ବାମ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"ଡାହାଣ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"ସକ୍ରିୟ"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"ସେଭ କରାଯାଇଛି"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"ସକ୍ରିୟ (କେବଳ ବାମ)"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 36a52b2..72e7760 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"ਖੱਬੇ ਪਾਸੇ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, ਸੱਜੇ ਪਾਸੇ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ਬੈਟਰੀ।"</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"ਖੱਬੇ ਪਾਸੇ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ਬੈਟਰੀ"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"ਸੱਜੇ ਪਾਸੇ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ਬੈਟਰੀ"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"ਖੱਬੇ ਪਾਸੇ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"ਸੱਜੇ ਪਾਸੇ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"ਕਿਰਿਆਸ਼ੀਲ"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"ਕਿਰਿਆਸ਼ੀਲ (ਸਿਰਫ਼ ਖੱਬਾ)"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index da68889..3b6d758 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -234,7 +234,7 @@
     <item msgid="4446831566506165093">"350%"</item>
     <item msgid="6946761421234586000">"400%"</item>
   </string-array>
-    <string name="choose_profile" msgid="343803890897657450">"Wybierz profil"</string>
+    <string name="choose_profile" msgid="343803890897657450">"Wybierz profil konta"</string>
     <string name="category_personal" msgid="6236798763159385225">"Osobiste"</string>
     <string name="category_work" msgid="4014193632325996115">"Służbowe"</string>
     <string name="category_private" msgid="4244892185452788977">"Prywatne"</string>
@@ -495,7 +495,7 @@
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Bateria będzie pełna do <xliff:g id="TIME">%3$s</xliff:g>"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Pełne naładowanie do <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Pełne naładowanie do <xliff:g id="TIME">%1$s</xliff:g>"</string>
-    <string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"Bateria będzie pełna do <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"Bateria będzie w pełni naładowana do <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Nieznane"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Ładowanie"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Szybkie ładowanie"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 2676ff6..d47a1f4 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Lado esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de bateria. Lado direito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de bateria."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Lado esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Lado direito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Lado esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Lado direito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Ativo"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Salvo"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Ativo (apenas o esquerdo)"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 2676ff6..d47a1f4 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Lado esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de bateria. Lado direito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de bateria."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Lado esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Lado direito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Lado esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Lado direito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Ativo"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Salvo"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Ativo (apenas o esquerdo)"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 4c97658..1c76fdd 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Nivelul bateriei din stânga: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, dreapta: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Nivelul bateriei din stânga: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Nivelul bateriei din dreapta:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Stânga: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Dreapta: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Activ"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Salvat"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Activ (numai stânga)"</string>
@@ -198,7 +196,7 @@
     <string name="launch_defaults_some" msgid="3631650616557252926">"Unele valori prestabilite sunt configurate"</string>
     <string name="launch_defaults_none" msgid="8049374306261262709">"Nu este configurată nicio valoare prestabilită"</string>
     <string name="tts_settings" msgid="8130616705989351312">"Setări redare vocală a textului"</string>
-    <string name="tts_settings_title" msgid="7602210956640483039">"Rezultatul redării vocale a textului"</string>
+    <string name="tts_settings_title" msgid="7602210956640483039">"Setări pentru redarea vocală a textului"</string>
     <string name="tts_default_rate_title" msgid="3964187817364304022">"Ritmul vorbirii"</string>
     <string name="tts_default_rate_summary" msgid="3781937042151716987">"Viteza cu care este vorbit textul"</string>
     <string name="tts_default_pitch_title" msgid="6988592215554485479">"Înălțime"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 505a1ec..2972e61 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> (Л), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> (П)."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> (Л)"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> (П)"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Левый <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Правый <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Активно"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Сохранено"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Используется (только левый)"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 6e4b5f9..9f1aca6 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"බැටරිය ව: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, ද: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"වම: බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"දකුණ: බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"වම <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"දකුණ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"ක්‍රියාකාරී"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"සුරැකිණි"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"සක්‍රිය (වම පමණි)"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 64c1113..db8301a 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Ľ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, P: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batérie."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Ľavá strana: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batérie"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Pravá strana: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batérie"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Ľavé: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Pravé: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktívne"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Uložené"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktívne (iba ľavé)"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 0288ec2..c33e425 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Majtas: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> bateri, djathtas: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> bateri."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Majtas: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> bateri"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Djathtas: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> bateri"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Majtas: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Djathtas: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktiv"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Të ruajtura"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktive (vetëm majtas)"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 927b8e4..fa94c2e 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Лево: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, десно: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> батерије."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Лево: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> батерије"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Десно: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> батерије"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Лево: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Десно: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Активан"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Сачувано"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Активно (само лево)"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 7c6a832..c4677be 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, H: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batteri."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Vänster: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Höger: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Vänster: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Höger <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Aktiv"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Sparad"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktiv (endast vänster)"</string>
@@ -237,7 +235,7 @@
     <item msgid="6946761421234586000">"400 %"</item>
   </string-array>
     <string name="choose_profile" msgid="343803890897657450">"Välj profil"</string>
-    <string name="category_personal" msgid="6236798763159385225">"Privat"</string>
+    <string name="category_personal" msgid="6236798763159385225">"Personlig"</string>
     <string name="category_work" msgid="4014193632325996115">"Jobb"</string>
     <string name="category_private" msgid="4244892185452788977">"Privat"</string>
     <string name="category_clone" msgid="1554511758987195974">"Klon"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 4b67011..ff62ba0 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"இடது பேட்டரி: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, வலது பேட்டரி: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"இடது: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> பேட்டரி"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"வலது: - <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> பேட்டரி"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"இடதுபுறம்: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"வலதுபுறம்: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"செயலில் உள்ளது"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"சேமிக்கப்பட்டது"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"செயலில் உள்ளது (இடதுபுறம் மட்டும்)"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index afda7cb..014ba26 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -235,7 +235,7 @@
     <item msgid="6946761421234586000">"400%"</item>
   </string-array>
     <string name="choose_profile" msgid="343803890897657450">"เลือกโปรไฟล์"</string>
-    <string name="category_personal" msgid="6236798763159385225">"ส่วนตัว"</string>
+    <string name="category_personal" msgid="6236798763159385225">"ส่วนบุคคล"</string>
     <string name="category_work" msgid="4014193632325996115">"งาน"</string>
     <string name="category_private" msgid="4244892185452788977">"ส่วนตัว"</string>
     <string name="category_clone" msgid="1554511758987195974">"โคลน"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 97d20bf..6aea659 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Sol: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Sağ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> pil seviyesi."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Sol: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> pil seviyesi"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Sağ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> pil seviyesi."</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Sol <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Sağ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Etkin"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Kaydedildi"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Etkin (yalnızca sol taraf)"</string>
@@ -239,7 +237,7 @@
     <string name="choose_profile" msgid="343803890897657450">"Profil seçin"</string>
     <string name="category_personal" msgid="6236798763159385225">"Kişisel"</string>
     <string name="category_work" msgid="4014193632325996115">"İş"</string>
-    <string name="category_private" msgid="4244892185452788977">"Gizli"</string>
+    <string name="category_private" msgid="4244892185452788977">"Özel"</string>
     <string name="category_clone" msgid="1554511758987195974">"Klon"</string>
     <string name="development_settings_title" msgid="140296922921597393">"Geliştirici seçenekleri"</string>
     <string name="development_settings_enable" msgid="4285094651288242183">"Geliştirici seçeneklerini etkinleştir"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index ab1e7f6..0400e0e 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Лівий: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, правий: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> заряду акумулятора."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Лівий: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> заряду акумулятора"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Правий: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> заряду акумулятора"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Ліва частина: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Права частина: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Активовано"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Збережено"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Активовано (лише лівий)"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index f0a89b3..f285086 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"Quvvat: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> (L), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> (R)."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Quvvat: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> (chap)"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Quvvat: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> (oʻng)."</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Chapda: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Oʻngda: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Faol"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Saqlangan"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Faol (faqat chap)"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index d85449e..d7cc91e 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -492,10 +492,10 @@
     <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> nữa là pin đầy"</string>
     <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> – Quá trình sạc được tối ưu hoá"</string>
     <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> – Đang sạc"</string>
-    <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Pin sẽ đầy vào <xliff:g id="TIME">%3$s</xliff:g>"</string>
-    <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Pin sẽ đầy vào <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Pin sẽ đầy vào <xliff:g id="TIME">%1$s</xliff:g>"</string>
-    <string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"Pin sẽ đầy vào <xliff:g id="TIME">%1$s</xliff:g>"</string>
+    <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATUS">%2$s</xliff:g> – Đến <xliff:g id="TIME">%3$s</xliff:g> pin sẽ đầy"</string>
+    <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> – Đến <xliff:g id="TIME">%2$s</xliff:g> pin sẽ đầy"</string>
+    <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Đến <xliff:g id="TIME">%1$s</xliff:g> pin sẽ đầy"</string>
+    <string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"Đến <xliff:g id="TIME">%1$s</xliff:g> pin sẽ đầy"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Không xác định"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Đang sạc"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Đang sạc nhanh"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 20fd651..7a355b9 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"左侧电池电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>,右侧电池电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>。"</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"左侧电池电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"右侧电池电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"左耳机电池电量 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"右耳机电池电量 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"使用中"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"已保存的设备"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"使用中(仅左耳助听器)"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 556c0af..d09a281 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -495,7 +495,7 @@
     <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - 在 <xliff:g id="TIME">%3$s</xliff:g>前充滿電"</string>
     <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> • 在 <xliff:g id="TIME">%2$s</xliff:g>前充滿電"</string>
     <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"在 <xliff:g id="TIME">%1$s</xliff:g>前充滿電"</string>
-    <string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"在 <xliff:g id="TIME">%1$s</xliff:g>前充滿電"</string>
+    <string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"<xliff:g id="TIME">%1$s</xliff:g> 前充滿電"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"未知"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"快速充電中"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 063d148..0a0fdff 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -103,10 +103,8 @@
     <string name="bluetooth_battery_level_untethered" msgid="1616774716076301755">"L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, R: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> ibhethri."</string>
     <string name="bluetooth_battery_level_untethered_left" msgid="5725764679536058365">"Kwesobunxele: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ibhethri"</string>
     <string name="bluetooth_battery_level_untethered_right" msgid="8377995536997790142">"Kwesokudla: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ibhethri"</string>
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_left (337629670583744410) -->
-    <skip />
-    <!-- no translation found for tv_bluetooth_battery_level_untethered_right (8610019317279155595) -->
-    <skip />
+    <string name="tv_bluetooth_battery_level_untethered_left" msgid="337629670583744410">"Kwesokunxele <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
+    <string name="tv_bluetooth_battery_level_untethered_right" msgid="8610019317279155595">"Kwesokudla <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"Iyasebenza"</string>
     <string name="bluetooth_saved_device" msgid="4895871321722311428">"Ilondoloziwe"</string>
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Iyasebenza (ngakwesokunxele kuphela)"</string>
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index ab04904..470cdee 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -32,7 +32,7 @@
 
     <!-- Usage graph dimens -->
     <dimen name="usage_graph_margin_top_bottom">9dp</dimen>
-    <dimen name="usage_graph_labels_width">56dp</dimen>
+    <dimen name="usage_graph_labels_width">60dp</dimen>
 
     <dimen name="usage_graph_divider_size">1dp</dimen>
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
index 869fb7f..7081195 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt
@@ -81,7 +81,7 @@
                     localMediaManager.unregisterCallback(callback)
                 }
             }
-            .shareIn(coroutineScope, SharingStarted.WhileSubscribed(), replay = 0)
+            .shareIn(coroutineScope, SharingStarted.Eagerly, replay = 0)
 
     override val currentConnectedDevice: StateFlow<MediaDevice?> =
         merge(devicesChanges, mediaDevicesUpdates)
@@ -89,8 +89,8 @@
             .onStart { emit(localMediaManager.currentConnectedDevice) }
             .stateIn(
                 coroutineScope,
-                SharingStarted.WhileSubscribed(),
-                localMediaManager.currentConnectedDevice
+                SharingStarted.Eagerly,
+                localMediaManager.currentConnectedDevice,
             )
 
     private sealed interface DevicesUpdate {
diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp
index e125083..397fab1 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -64,6 +64,8 @@
         timeout: 36000,
     },
     upstream: true,
+
+    strict_mode: false,
 }
 
 java_genrule {
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index f83928d..03c2a83 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -418,6 +418,7 @@
         VALIDATORS.put(Global.Wearable.CHARGING_SOUNDS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.Wearable.BEDTIME_MODE, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.Wearable.BEDTIME_HARD_MODE, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.Wearable.VIBRATE_FOR_ACTIVE_UNLOCK, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.Wearable.DYNAMIC_COLOR_THEME_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.Wearable.SCREENSHOT_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.Wearable.UPGRADE_DATA_MIGRATION_STATUS,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index 04922d6..c8992c3 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -64,6 +64,7 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.InputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.nio.file.Files;
@@ -85,13 +86,13 @@
 // FOR ACONFIGD TEST MISSION AND ROLLOUT
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
-import android.net.LocalSocketAddress;
-import android.net.LocalSocket;
 import android.util.proto.ProtoInputStream;
 import android.aconfigd.Aconfigd.StorageRequestMessage;
 import android.aconfigd.Aconfigd.StorageRequestMessages;
 import android.aconfigd.Aconfigd.StorageReturnMessage;
 import android.aconfigd.Aconfigd.StorageReturnMessages;
+import android.aconfigd.AconfigdClientSocket;
+import android.aconfigd.AconfigdFlagInfo;
 import android.aconfigd.AconfigdJavaUtils;
 import static com.android.aconfig_new_storage.Flags.enableAconfigStorageDaemon;
 /**
@@ -265,6 +266,10 @@
     @NonNull
     private Map<String, Map<String, String>> mNamespaceDefaults;
 
+    // TOBO(b/312444587): remove the comparison logic after Test Mission 2.
+    @NonNull
+    private Map<String, AconfigdFlagInfo> mAconfigDefaultFlags;
+
     public static final int SETTINGS_TYPE_GLOBAL = 0;
     public static final int SETTINGS_TYPE_SYSTEM = 1;
     public static final int SETTINGS_TYPE_SECURE = 2;
@@ -334,8 +339,13 @@
                 + settingTypeToString(getTypeFromKey(key)) + "]";
     }
 
-    public SettingsState(Context context, Object lock, File file, int key,
-            int maxBytesPerAppPackage, Looper looper) {
+    public SettingsState(
+            Context context,
+            Object lock,
+            File file,
+            int key,
+            int maxBytesPerAppPackage,
+            Looper looper) {
         // It is important that we use the same lock as the settings provider
         // to ensure multiple mutations on this state are atomically persisted
         // as the async persistence should be blocked while we make changes.
@@ -353,12 +363,15 @@
             mPackageToMemoryUsage = null;
         }
 
-        mHistoricalOperations = Build.IS_DEBUGGABLE
-                ? new ArrayList<>(HISTORICAL_OPERATION_COUNT) : null;
+        mHistoricalOperations =
+                Build.IS_DEBUGGABLE ? new ArrayList<>(HISTORICAL_OPERATION_COUNT) : null;
 
         mNamespaceDefaults = new HashMap<>();
+        mAconfigDefaultFlags = new HashMap<>();
 
         ProtoOutputStream requests = null;
+        Map<String, AconfigdFlagInfo> aconfigFlagMap = new HashMap<>();
+
         synchronized (mLock) {
             readStateSyncLocked();
 
@@ -375,39 +388,114 @@
                 }
             }
 
+            if (enableAconfigStorageDaemon()) {
+                if (isConfigSettingsKey(mKey)) {
+                    aconfigFlagMap = getAllAconfigFlagsFromSettings();
+                }
+            }
+
             if (isConfigSettingsKey(mKey)) {
-                requests = handleBulkSyncToNewStorage();
+                requests = handleBulkSyncToNewStorage(aconfigFlagMap);
             }
         }
 
-        if (requests != null) {
-            LocalSocket client = new LocalSocket();
-            try{
-                client.connect(new LocalSocketAddress(
-                    "aconfigd", LocalSocketAddress.Namespace.RESERVED));
-                Slog.d(LOG_TAG, "connected to aconfigd socket");
-            } catch (IOException ioe) {
-                Slog.e(LOG_TAG, "failed to connect to aconfigd socket", ioe);
-                return;
+        if (enableAconfigStorageDaemon()) {
+            if (isConfigSettingsKey(mKey)){
+                AconfigdClientSocket localSocket = AconfigdJavaUtils.getAconfigdClientSocket();
+                if (requests != null) {
+                    InputStream res = localSocket.send(requests.getBytes());
+                    if (res == null) {
+                        Slog.w(LOG_TAG, "Bulk sync request to acongid failed.");
+                    }
+                }
+                // TOBO(b/312444587): remove the comparison logic after Test Mission 2.
+                if (mSettings.get("aconfigd_marker/bulk_synced").value.equals("true")
+                        && requests == null) {
+                    Map<String, AconfigdFlagInfo> aconfigdFlagMap =
+                            AconfigdJavaUtils.listFlagsValueInNewStorage(localSocket);
+                    compareFlagValueInNewStorage(
+                            aconfigFlagMap,
+                            mAconfigDefaultFlags,
+                            aconfigdFlagMap);
+                }
             }
-            AconfigdJavaUtils.sendAconfigdRequests(client, requests);
         }
     }
 
-    // TODO(b/341764371): migrate aconfig flag push to GMS core
-    public static class FlagOverrideToSync {
-        public String packageName;
-        public String flagName;
-        public String flagValue;
-        public boolean isLocal;
+    // TOBO(b/312444587): remove the comparison logic after Test Mission 2.
+    public int compareFlagValueInNewStorage(
+            Map<String, AconfigdFlagInfo> settingFlagMap,
+            Map<String, AconfigdFlagInfo> defaultFlagMap,
+            Map<String, AconfigdFlagInfo> aconfigdFlagMap) {
+
+        // Get all defaults from the default map. The mSettings may not contain
+        // all flags, since it only contains updated flags.
+        int diffNum = 0;
+        for (Map.Entry<String, AconfigdFlagInfo> entry : defaultFlagMap.entrySet()) {
+            String key = entry.getKey();
+            AconfigdFlagInfo flag = entry.getValue();
+            if (settingFlagMap.containsKey(key)) {
+                flag.merge(settingFlagMap.get(key));
+            }
+
+            AconfigdFlagInfo aconfigdFlag = aconfigdFlagMap.get(key);
+            if (aconfigdFlag == null) {
+                Slog.w(LOG_TAG, String.format("Flag %s is missing from aconfigd", key));
+                diffNum++;
+                continue;
+            }
+            String diff = flag.dumpDiff(aconfigdFlag);
+            if (!diff.isEmpty()) {
+                Slog.w(
+                        LOG_TAG,
+                        String.format(
+                                "Flag %s is different in Settings and aconfig: %s", key, diff));
+                diffNum++;
+            }
+        }
+
+        for (String key : aconfigdFlagMap.keySet()) {
+            if (defaultFlagMap.containsKey(key)) continue;
+            Slog.w(LOG_TAG, String.format("Flag %s is missing from Settings", key));
+            diffNum++;
+        }
+
+        if (diffNum == 0) {
+            Slog.i(LOG_TAG, "Settings and new storage have same flags.");
+        }
+        return diffNum;
+    }
+
+    @GuardedBy("mLock")
+    public Map<String, AconfigdFlagInfo> getAllAconfigFlagsFromSettings() {
+        Map<String, AconfigdFlagInfo> ret = new HashMap<>();
+        int numSettings = mSettings.size();
+        int num_requests = 0;
+        for (int i = 0; i < numSettings; i++) {
+            String name = mSettings.keyAt(i);
+            Setting setting = mSettings.valueAt(i);
+            AconfigdFlagInfo flag =
+                    getFlagOverrideToSync(name, setting.getValue());
+            if (flag == null) {
+                continue;
+            }
+            String fullFlagName = flag.getFullFlagName();
+            AconfigdFlagInfo prev = ret.putIfAbsent(fullFlagName,flag);
+            if (prev != null) {
+                prev.merge(flag);
+            }
+            ++num_requests;
+        }
+        Slog.i(LOG_TAG, num_requests + " flag override requests created");
+        return ret;
     }
 
     // TODO(b/341764371): migrate aconfig flag push to GMS core
     @VisibleForTesting
     @GuardedBy("mLock")
-    public FlagOverrideToSync getFlagOverrideToSync(String name, String value) {
+    public AconfigdFlagInfo getFlagOverrideToSync(String name, String value) {
         int slashIdx = name.indexOf("/");
-        if (slashIdx <= 0 || slashIdx >= name.length()-1) {
+        if (slashIdx <= 0 || slashIdx >= name.length() - 1) {
             Slog.e(LOG_TAG, "invalid flag name " + name);
             return null;
         }
@@ -430,8 +518,9 @@
         }
 
         String aconfigName = namespace + "/" + fullFlagName;
-        boolean isAconfig = mNamespaceDefaults.containsKey(namespace)
-                            && mNamespaceDefaults.get(namespace).containsKey(aconfigName);
+        boolean isAconfig =
+                mNamespaceDefaults.containsKey(namespace)
+                        && mNamespaceDefaults.get(namespace).containsKey(aconfigName);
         if (!isAconfig) {
             return null;
         }
@@ -443,25 +532,30 @@
             return null;
         }
 
-        FlagOverrideToSync flag = new FlagOverrideToSync();
-        flag.packageName = fullFlagName.substring(0, dotIdx);
-        flag.flagName = fullFlagName.substring(dotIdx + 1);
-        flag.isLocal = isLocal;
-        flag.flagValue = value;
-        return flag;
+        AconfigdFlagInfo.Builder builder = AconfigdFlagInfo.newBuilder()
+                        .setPackageName(fullFlagName.substring(0, dotIdx))
+                        .setFlagName(fullFlagName.substring(dotIdx + 1))
+                        .setDefaultFlagValue(mNamespaceDefaults.get(namespace).get(aconfigName));
+
+        if (isLocal) {
+            builder.setHasLocalOverride(isLocal).setBootFlagValue(value).setLocalFlagValue(value);
+        } else {
+            builder.setHasServerOverride(true).setServerFlagValue(value).setBootFlagValue(value);
+        }
+        return builder.build();
     }
 
 
     // TODO(b/341764371): migrate aconfig flag push to GMS core
     @VisibleForTesting
     @GuardedBy("mLock")
-    public ProtoOutputStream handleBulkSyncToNewStorage() {
+    public ProtoOutputStream handleBulkSyncToNewStorage(
+            Map<String, AconfigdFlagInfo> aconfigFlagMap) {
         // get marker or add marker if it does not exist
         final String bulkSyncMarkerName = new String("aconfigd_marker/bulk_synced");
         Setting markerSetting = mSettings.get(bulkSyncMarkerName);
         if (markerSetting == null) {
-            markerSetting = new Setting(
-                bulkSyncMarkerName, "false", false, "aconfig", "aconfig");
+            markerSetting = new Setting(bulkSyncMarkerName, "false", false, "aconfig", "aconfig");
             mSettings.put(bulkSyncMarkerName, markerSetting);
         }
 
@@ -479,24 +573,19 @@
                 AconfigdJavaUtils.writeResetStorageRequest(requests);
 
                 // loop over all settings and add flag override requests
-                final int numSettings = mSettings.size();
-                int num_requests = 0;
-                for (int i = 0; i < numSettings; i++) {
-                    String name = mSettings.keyAt(i);
-                    Setting setting = mSettings.valueAt(i);
-                    FlagOverrideToSync flag =
-                            getFlagOverrideToSync(name, setting.getValue());
-                    if (flag == null) {
-                        continue;
-                    }
-                    ++num_requests;
+                for (AconfigdFlagInfo flag : aconfigFlagMap.values()) {
+                    String value =
+                            flag.getHasLocalOverride()
+                                    ? flag.getLocalFlagValue()
+                                    : flag.getServerFlagValue();
                     AconfigdJavaUtils.writeFlagOverrideRequest(
-                        requests, flag.packageName, flag.flagName, flag.flagValue,
-                        flag.isLocal);
+                            requests,
+                            flag.getPackageName(),
+                            flag.getFlagName(),
+                            value,
+                            flag.getHasLocalOverride());
                 }
 
-                Slog.i(LOG_TAG, num_requests + " flag override requests created");
-
                 // mark sync has been done
                 markerSetting.value = "true";
                 scheduleWriteIfNeededLocked();
@@ -513,14 +602,14 @@
                 return null;
             }
         }
-
     }
 
     @GuardedBy("mLock")
     private void loadAconfigDefaultValuesLocked(List<String> filePaths) {
         for (String fileName : filePaths) {
             try (FileInputStream inputStream = new FileInputStream(fileName)) {
-                loadAconfigDefaultValues(inputStream.readAllBytes(), mNamespaceDefaults);
+                loadAconfigDefaultValues(
+                        inputStream.readAllBytes(), mNamespaceDefaults, mAconfigDefaultFlags);
             } catch (IOException e) {
                 Slog.e(LOG_TAG, "failed to read protobuf", e);
             }
@@ -566,21 +655,30 @@
 
     @VisibleForTesting
     @GuardedBy("mLock")
-    public static void loadAconfigDefaultValues(byte[] fileContents,
-            @NonNull Map<String, Map<String, String>> defaultMap) {
+    public static void loadAconfigDefaultValues(
+            byte[] fileContents,
+            @NonNull Map<String, Map<String, String>> defaultMap,
+            @NonNull Map<String, AconfigdFlagInfo> flagInfoDefault) {
         try {
-            parsed_flags parsedFlags =
-                    parsed_flags.parseFrom(fileContents);
+            parsed_flags parsedFlags = parsed_flags.parseFrom(fileContents);
             for (parsed_flag flag : parsedFlags.getParsedFlagList()) {
                 if (!defaultMap.containsKey(flag.getNamespace())) {
                     Map<String, String> defaults = new HashMap<>();
                     defaultMap.put(flag.getNamespace(), defaults);
                 }
-                String flagName = flag.getNamespace()
-                        + "/" + flag.getPackage() + "." + flag.getName();
-                String flagValue = flag.getState() == flag_state.ENABLED
-                        ? "true" : "false";
+                String fullFlagName = flag.getPackage() + "." + flag.getName();
+                String flagName = flag.getNamespace() + "/" + fullFlagName;
+                String flagValue = flag.getState() == flag_state.ENABLED ? "true" : "false";
                 defaultMap.get(flag.getNamespace()).put(flagName, flagValue);
+                if (!flagInfoDefault.containsKey(fullFlagName)) {
+                    flagInfoDefault.put(
+                            fullFlagName,
+                            AconfigdFlagInfo.newBuilder()
+                                    .setPackageName(flag.getPackage())
+                                    .setFlagName(flag.getName())
+                                    .setDefaultFlagValue(flagValue)
+                                    .build());
+                }
             }
         } catch (IOException e) {
             Slog.e(LOG_TAG, "failed to parse protobuf", e);
@@ -1646,7 +1744,6 @@
                         }
                     }
                 }
-
                 mSettings.put(name, new Setting(name, value, defaultValue, packageName, tag,
                         fromSystem, id, isPreservedInRestore));
 
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 473955f..4ec170d 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -619,6 +619,7 @@
                     Settings.Global.Wearable.COOLDOWN_MODE_ON,
                     Settings.Global.Wearable.BEDTIME_MODE,
                     Settings.Global.Wearable.BEDTIME_HARD_MODE,
+                    Settings.Global.Wearable.VIBRATE_FOR_ACTIVE_UNLOCK,
                     Settings.Global.Wearable.LOCK_SCREEN_STATE,
                     Settings.Global.Wearable.DISABLE_AOD_WHILE_PLUGGED,
                     Settings.Global.Wearable.NETWORK_LOCATION_OPT_IN,
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java
index ca1e4c1..e4898da 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderMultiUsersTest.java
@@ -27,11 +27,11 @@
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.bedstead.enterprise.annotations.EnsureHasNoWorkProfile;
+import com.android.bedstead.enterprise.annotations.EnsureHasWorkProfile;
 import com.android.bedstead.harrier.BedsteadJUnit4;
 import com.android.bedstead.harrier.DeviceState;
-import com.android.bedstead.harrier.annotations.EnsureHasNoWorkProfile;
 import com.android.bedstead.harrier.annotations.EnsureHasSecondaryUser;
-import com.android.bedstead.harrier.annotations.EnsureHasWorkProfile;
 import com.android.bedstead.harrier.annotations.RequireFeature;
 import com.android.bedstead.harrier.annotations.RequireRunOnInitialUser;
 import com.android.bedstead.harrier.annotations.RequireRunOnPrimaryUser;
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
index 244c8c4..256b999 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
@@ -24,13 +24,13 @@
 import android.aconfig.Aconfig;
 import android.aconfig.Aconfig.parsed_flag;
 import android.aconfig.Aconfig.parsed_flags;
+import android.aconfigd.AconfigdFlagInfo;
 import android.os.Looper;
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.util.Xml;
 import android.util.proto.ProtoOutputStream;
-import com.android.providers.settings.SettingsState.FlagOverrideToSync;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
@@ -145,16 +145,32 @@
                         .setState(Aconfig.flag_state.ENABLED)
                         .setPermission(Aconfig.flag_permission.READ_WRITE))
                 .build();
+        
+        AconfigdFlagInfo flag1 = AconfigdFlagInfo.newBuilder()
+                                                .setPackageName("com.android.flags")
+                                                .setFlagName("flag1")
+                                                .setDefaultFlagValue("false")
+                                                .build();
+        AconfigdFlagInfo flag2 = AconfigdFlagInfo.newBuilder()
+                                                .setPackageName("com.android.flags")
+                                                .setFlagName("flag2")
+                                                .setDefaultFlagValue("true")
+                                                .build();
+        Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
 
         synchronized (lock) {
             Map<String, Map<String, String>> defaults = new HashMap<>();
-            settingsState.loadAconfigDefaultValues(flags.toByteArray(), defaults);
+            settingsState.loadAconfigDefaultValues(
+                flags.toByteArray(), defaults, flagInfoDefault);
             Map<String, String> namespaceDefaults = defaults.get("test_namespace");
             assertEquals(2, namespaceDefaults.keySet().size());
 
             assertEquals("false", namespaceDefaults.get("test_namespace/com.android.flags.flag1"));
             assertEquals("true", namespaceDefaults.get("test_namespace/com.android.flags.flag2"));
         }
+
+        assertEquals(flag1, flagInfoDefault.get(flag1.getFullFlagName()));
+        assertEquals(flag2, flagInfoDefault.get(flag2.getFullFlagName()));
     }
 
     @Test
@@ -165,6 +181,8 @@
                 InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey,
                 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
 
+        Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
+
         parsed_flags flags = parsed_flags
                 .newBuilder()
                 .addParsedFlag(parsed_flag
@@ -177,7 +195,8 @@
 
         synchronized (lock) {
             Map<String, Map<String, String>> defaults = new HashMap<>();
-            settingsState.loadAconfigDefaultValues(flags.toByteArray(), defaults);
+            settingsState.loadAconfigDefaultValues(
+                flags.toByteArray(), defaults, flagInfoDefault);
 
             Map<String, String> namespaceDefaults = defaults.get("test_namespace");
             assertEquals(null, namespaceDefaults);
@@ -204,10 +223,12 @@
                         .setState(Aconfig.flag_state.DISABLED)
                         .setPermission(Aconfig.flag_permission.READ_WRITE))
                 .build();
+        Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
 
         synchronized (lock) {
             Map<String, Map<String, String>> defaults = new HashMap<>();
-            settingsState.loadAconfigDefaultValues(flags.toByteArray(), defaults);
+            settingsState.loadAconfigDefaultValues(
+                flags.toByteArray(), defaults, flagInfoDefault);
             settingsState.addAconfigDefaultValuesFromMap(defaults);
 
             settingsState.insertSettingLocked("test_namespace/com.android.flags.flag5",
@@ -238,8 +259,10 @@
     @Test
     public void testInvalidAconfigProtoDoesNotCrash() {
         Map<String, Map<String, String>> defaults = new HashMap<>();
+        Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
         SettingsState settingsState = getSettingStateObject();
-        settingsState.loadAconfigDefaultValues("invalid protobuf".getBytes(), defaults);
+        settingsState.loadAconfigDefaultValues(
+            "invalid protobuf".getBytes(), defaults, flagInfoDefault);
     }
 
     @Test
@@ -759,6 +782,8 @@
         Map<String, String> keyValues =
                 Map.of("test_namespace/com.android.flags.flag3", "true");
 
+        Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
+
         parsed_flags flags = parsed_flags
                 .newBuilder()
                 .addParsedFlag(parsed_flag
@@ -774,7 +799,8 @@
 
         synchronized (mLock) {
             settingsState.loadAconfigDefaultValues(
-                    flags.toByteArray(), settingsState.getAconfigDefaultValues());
+                    flags.toByteArray(),
+                    settingsState.getAconfigDefaultValues(), flagInfoDefault);
             List<String> updates =
                     settingsState.setSettingsLocked("test_namespace/", keyValues, packageName);
             assertEquals(1, updates.size());
@@ -840,10 +866,13 @@
                         .setState(Aconfig.flag_state.DISABLED)
                         .setPermission(Aconfig.flag_permission.READ_WRITE))
                 .build();
+        Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
 
         synchronized (mLock) {
             settingsState.loadAconfigDefaultValues(
-                    flags.toByteArray(), settingsState.getAconfigDefaultValues());
+                    flags.toByteArray(),
+                    settingsState.getAconfigDefaultValues(),
+                    flagInfoDefault);
             List<String> updates =
                     settingsState.setSettingsLocked("test_namespace/", keyValues, packageName);
             assertEquals(3, updates.size());
@@ -973,10 +1002,12 @@
                         .setState(Aconfig.flag_state.DISABLED)
                         .setPermission(Aconfig.flag_permission.READ_WRITE))
                 .build();
+        Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
 
         synchronized (lock) {
             Map<String, Map<String, String>> defaults = new HashMap<>();
-            settingsState.loadAconfigDefaultValues(flags.toByteArray(), defaults);
+            settingsState.loadAconfigDefaultValues(
+                flags.toByteArray(), defaults, flagInfoDefault);
             Map<String, String> namespaceDefaults = defaults.get("test_namespace");
             assertEquals(1, namespaceDefaults.keySet().size());
             settingsState.addAconfigDefaultValuesFromMap(defaults);
@@ -991,22 +1022,28 @@
             "some_namespace/some_flag", "false") == null);
 
         // server override
-        FlagOverrideToSync flag = settingsState.getFlagOverrideToSync(
+        AconfigdFlagInfo flag = settingsState.getFlagOverrideToSync(
             "test_namespace/com.android.flags.flag1", "false");
         assertTrue(flag != null);
-        assertEquals(flag.packageName, "com.android.flags");
-        assertEquals(flag.flagName, "flag1");
-        assertEquals(flag.flagValue, "false");
-        assertEquals(flag.isLocal, false);
+        assertEquals(flag.getPackageName(), "com.android.flags");
+        assertEquals(flag.getFlagName(), "flag1");
+        assertEquals("false", flag.getBootFlagValue());
+        assertEquals("false", flag.getServerFlagValue());
+        assertFalse(flag.getHasLocalOverride());
+        assertNull(flag.getLocalFlagValue());
+        assertEquals("false", flag.getDefaultFlagValue());
 
         // local override
         flag = settingsState.getFlagOverrideToSync(
             "device_config_overrides/test_namespace:com.android.flags.flag1", "false");
         assertTrue(flag != null);
-        assertEquals(flag.packageName, "com.android.flags");
-        assertEquals(flag.flagName, "flag1");
-        assertEquals(flag.flagValue, "false");
-        assertEquals(flag.isLocal, true);
+        assertEquals(flag.getPackageName(), "com.android.flags");
+        assertEquals(flag.getFlagName(), "flag1");
+        assertEquals("false", flag.getLocalFlagValue());
+        assertEquals("false", flag.getBootFlagValue());
+        assertTrue(flag.getHasLocalOverride());
+        assertNull(flag.getServerFlagValue());
+        assertEquals("false", flag.getDefaultFlagValue());
     }
 
     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -1020,18 +1057,25 @@
                 InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey,
                 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
 
+        Map<String, AconfigdFlagInfo> flags = new HashMap<>();
+        AconfigdFlagInfo flag = AconfigdFlagInfo.newBuilder()
+        .setPackageName("com.android.flags")
+                .setFlagName("flag1")
+                .setBootFlagValue("true").build();
+        flags.put("com.android.flags/flag1", flag);
+
         synchronized (lock) {
             settingsState.insertSettingLocked("aconfigd_marker/bulk_synced",
                     "false", null, false, "aconfig");
 
             // first bulk sync
-            ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage();
+            ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage(flags);
             assertTrue(requests != null);
             String value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
             assertEquals("true", value);
 
             // send time should no longer bulk sync
-            requests = settingsState.handleBulkSyncToNewStorage();
+            requests = settingsState.handleBulkSyncToNewStorage(flags);
             assertTrue(requests == null);
             value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
             assertEquals("true", value);
@@ -1047,21 +1091,200 @@
                 InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey,
                 SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
 
+        Map<String, AconfigdFlagInfo> flags = new HashMap<>();
         synchronized (lock) {
             settingsState.insertSettingLocked("aconfigd_marker/bulk_synced",
                     "true", null, false, "aconfig");
 
             // when aconfigd is off, should change the marker to false
-            ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage();
+            ProtoOutputStream requests = settingsState.handleBulkSyncToNewStorage(flags);
             assertTrue(requests == null);
             String value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
             assertEquals("false", value);
 
             // marker started with false value, after call, it should remain false
-            requests = settingsState.handleBulkSyncToNewStorage();
+            requests = settingsState.handleBulkSyncToNewStorage(flags);
             assertTrue(requests == null);
             value = settingsState.getSettingLocked("aconfigd_marker/bulk_synced").getValue();
             assertEquals("false", value);
         }
     }
+
+    @Test
+    public void testGetAllAconfigFlagsFromSettings() throws Exception {
+        final Object lock = new Object();
+        final PrintStream os = new PrintStream(new FileOutputStream(mSettingsFile));
+        os.print(
+                "<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>"
+                        + "<settings version=\"120\">"
+                        + "  <setting id=\"0\" name=\"test_namespace/com.android.flags.flag1\" "
+                            + "value=\"false\" package=\"com.android.flags\" />"
+                        + "  <setting id=\"1\" name=\"device_config_overrides/test_namespace:com.android.flags.flag1\" "
+                            + "value=\"true\" package=\"com.android.flags\" />"
+                        + "  <setting id=\"2\" name=\"device_config_overrides/test_namespace:com.android.flags.flag2\" "
+                            + "value=\"true\" package=\"com.android.flags\" />"
+                        + "  <setting id=\"3\" name=\"test_namespace/com.android.flags.flag3\" "
+                            + "value=\"true\" package=\"com.android.flags\" />"
+                        + "</settings>");
+        os.close();
+
+        int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
+
+        SettingsState settingsState = new SettingsState(
+                InstrumentationRegistry.getContext(), lock, mSettingsFile, configKey,
+                SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
+
+        Map<String, AconfigdFlagInfo> ret;
+        synchronized (lock) {
+            ret = settingsState.getAllAconfigFlagsFromSettings();
+        }
+
+        assertTrue(ret.isEmpty());
+
+        parsed_flags flags =
+                parsed_flags
+                        .newBuilder()
+                        .addParsedFlag(
+                                parsed_flag
+                                        .newBuilder()
+                                        .setPackage("com.android.flags")
+                                        .setName("flag1")
+                                        .setNamespace("test_namespace")
+                                        .setDescription("test flag")
+                                        .addBug("12345678")
+                                        .setState(Aconfig.flag_state.DISABLED)
+                                        .setPermission(Aconfig.flag_permission.READ_WRITE))
+                        .addParsedFlag(
+                                parsed_flag
+                                        .newBuilder()
+                                        .setPackage("com.android.flags")
+                                        .setName("flag2")
+                                        .setNamespace("test_namespace")
+                                        .setDescription("test flag")
+                                        .addBug("12345678")
+                                        .setState(Aconfig.flag_state.DISABLED)
+                                        .setPermission(Aconfig.flag_permission.READ_WRITE))
+                        .addParsedFlag(
+                                parsed_flag
+                                        .newBuilder()
+                                        .setPackage("com.android.flags")
+                                        .setName("flag3")
+                                        .setNamespace("test_namespace")
+                                        .setDescription("test flag")
+                                        .addBug("12345678")
+                                        .setState(Aconfig.flag_state.DISABLED)
+                                        .setPermission(Aconfig.flag_permission.READ_WRITE))
+                        .build();
+
+        Map<String, Map<String, String>> defaults = new HashMap<>();
+        Map<String, AconfigdFlagInfo> flagInfoDefault = new HashMap<>();
+        synchronized (lock) {
+            settingsState.loadAconfigDefaultValues(
+                flags.toByteArray(), defaults, flagInfoDefault);
+            settingsState.addAconfigDefaultValuesFromMap(defaults);
+            ret = settingsState.getAllAconfigFlagsFromSettings();
+        }
+
+        AconfigdFlagInfo expectedFlag1 =
+                AconfigdFlagInfo.newBuilder()
+                        .setPackageName("com.android.flags")
+                        .setFlagName("flag1")
+                        .setServerFlagValue("false")
+                        .setLocalFlagValue("true")
+                        .setDefaultFlagValue("false")
+                        .setBootFlagValue("true")
+                        .setHasServerOverride(true)
+                        .setHasLocalOverride(true)
+                        .setIsReadWrite(false)
+                        .build();
+
+        AconfigdFlagInfo expectedFlag2 =
+                AconfigdFlagInfo.newBuilder()
+                        .setPackageName("com.android.flags")
+                        .setFlagName("flag2")
+                        .setLocalFlagValue("true")
+                        .setDefaultFlagValue("false")
+                        .setBootFlagValue("true")
+                        .setHasLocalOverride(true)
+                        .setHasServerOverride(false)
+                        .setIsReadWrite(false)
+                        .build();
+
+
+        AconfigdFlagInfo expectedFlag3 =
+                AconfigdFlagInfo.newBuilder()
+                        .setPackageName("com.android.flags")
+                        .setFlagName("flag3")
+                        .setServerFlagValue("true")
+                        .setBootFlagValue("true")
+                        .setDefaultFlagValue("false")
+                        .setHasServerOverride(true)
+                        .setIsReadWrite(false)
+                        .build();
+
+        assertEquals(expectedFlag1, ret.get("com.android.flags.flag1"));
+        assertEquals(expectedFlag2, ret.get("com.android.flags.flag2"));
+        assertEquals(expectedFlag3, ret.get("com.android.flags.flag3"));
+    }
+
+    @Test
+    public void testCompareFlagValueInNewStorage() {
+                int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0);
+        Object lock = new Object();
+        SettingsState settingsState =
+                new SettingsState(
+                        InstrumentationRegistry.getContext(),
+                        lock,
+                        mSettingsFile,
+                        configKey,
+                        SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED,
+                        Looper.getMainLooper());
+
+        AconfigdFlagInfo defaultFlag1 =
+                AconfigdFlagInfo.newBuilder()
+                        .setPackageName("com.android.flags")
+                        .setFlagName("flag1")
+                        .setDefaultFlagValue("false")
+                        .build();
+
+        AconfigdFlagInfo settingFlag1 =
+                AconfigdFlagInfo.newBuilder()
+                        .setPackageName("com.android.flags")
+                        .setFlagName("flag1")
+                        .setServerFlagValue("true")
+                        .setHasServerOverride(true)
+                        .build();
+
+        AconfigdFlagInfo expectedFlag1 =
+                AconfigdFlagInfo.newBuilder()
+                        .setPackageName("com.android.flags")
+                        .setFlagName("flag1")
+                        .setBootFlagValue("true")
+                        .setServerFlagValue("true")
+                        .setDefaultFlagValue("false")
+                        .setHasServerOverride(true)
+                        .build();
+
+        Map<String, AconfigdFlagInfo> settingMap = new HashMap<>();
+        Map<String, AconfigdFlagInfo> aconfigdMap = new HashMap<>();
+        Map<String, AconfigdFlagInfo> defaultMap = new HashMap<>();
+
+        defaultMap.put("com.android.flags.flag1", defaultFlag1);
+        settingMap.put("com.android.flags.flag1", settingFlag1);
+        aconfigdMap.put("com.android.flags.flag1", expectedFlag1);
+
+        int ret = settingsState.compareFlagValueInNewStorage(settingMap, defaultMap, aconfigdMap);
+        assertEquals(0, ret);
+
+        AconfigdFlagInfo defaultFlag2 =
+                AconfigdFlagInfo.newBuilder()
+                        .setPackageName("com.android.flags")
+                        .setFlagName("flag2")
+                        .setDefaultFlagValue("false")
+                        .build();
+        defaultMap.put("com.android.flags.flag2", defaultFlag2);
+
+        ret = settingsState.compareFlagValueInNewStorage(settingMap, defaultMap, aconfigdMap);
+        assertEquals(1, ret);
+    }
 }
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index e940674..07a00fb 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -78,15 +78,44 @@
     visibility: ["//visibility:private"],
 }
 
+// Tests where robolectric failed at runtime. (go/multivalent-tests)
 filegroup {
     name: "SystemUI-tests-broken-robofiles-run",
     srcs: [
-        "tests/src/**/systemui/util/LifecycleFragmentTest.java",
-        "tests/src/**/systemui/util/TestableAlertDialogTest.kt",
-        "tests/src/**/systemui/util/kotlin/PairwiseFlowTest",
-        "tests/src/**/systemui/util/sensors/AsyncManagerTest.java",
-        "tests/src/**/systemui/util/sensors/ThresholdSensorImplTest.java",
-        "tests/src/**/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java",
+        "tests/src/**/systemui/globalactions/GlobalActionsColumnLayoutTest.java",
+        "tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java",
+        "tests/src/**/systemui/globalactions/GlobalActionsImeTest.java",
+        "tests/src/**/systemui/graphics/ImageLoaderTest.kt",
+        "tests/src/**/systemui/keyguard/CustomizationProviderTest.kt",
+        "tests/src/**/systemui/keyguard/KeyguardViewMediatorTest.java",
+        "tests/src/**/systemui/keyguard/LifecycleTest.java",
+        "tests/src/**/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt",
+        "tests/src/**/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt",
+        "tests/src/**/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt",
+        "tests/src/**/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt",
+        "tests/src/**/systemui/lifecycle/RepeatWhenAttachedTest.kt",
+        "tests/src/**/systemui/log/LogBufferTest.kt",
+        "tests/src/**/systemui/media/dialog/MediaOutputBaseDialogTest.java",
+        "tests/src/**/systemui/media/dialog/MediaOutputBroadcastDialogTest.java",
+        "tests/src/**/systemui/media/dialog/MediaOutputDialogTest.java",
+        "tests/src/**/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt",
+        "tests/src/**/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt",
+        "tests/src/**/systemui/navigationbar/NavigationBarButtonTest.java",
+        "tests/src/**/systemui/people/PeopleProviderTest.java",
+        "tests/src/**/systemui/people/PeopleSpaceUtilsTest.java",
+        "tests/src/**/systemui/people/widget/PeopleSpaceWidgetManagerTest.java",
+        "tests/src/**/systemui/people/PeopleTileViewHelperTest.java",
+        "tests/src/**/systemui/power/data/repository/PowerRepositoryImplTest.kt",
+        "tests/src/**/systemui/privacy/PrivacyConfigFlagsTest.kt",
+        "tests/src/**/systemui/privacy/PrivacyDialogV2Test.kt",
+        "tests/src/**/systemui/qs/external/TileRequestDialogEventLoggerTest.kt",
+        "tests/src/**/systemui/qs/AutoAddTrackerTest.kt",
+        "tests/src/**/systemui/qs/external/TileRequestDialogEventLoggerTest.kt",
+        "tests/src/**/systemui/qs/tiles/DndTileTest.kt",
+        "tests/src/**/systemui/qs/tiles/DreamTileTest.java",
+        "tests/src/**/systemui/qs/FgsManagerControllerTest.java",
+        "tests/src/**/systemui/qs/QSPanelTest.kt",
+        "tests/src/**/systemui/reardisplay/RearDisplayDialogControllerTest.java",
         "tests/src/**/systemui/statusbar/KeyboardShortcutListSearchTest.java",
         "tests/src/**/systemui/statusbar/KeyboardShortcutsTest.java",
         "tests/src/**/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt",
@@ -133,6 +162,17 @@
         "tests/src/**/systemui/statusbar/policy/RemoteInputViewTest.java",
         "tests/src/**/systemui/statusbar/policy/SmartReplyViewTest.java",
         "tests/src/**/systemui/statusbar/StatusBarStateControllerImplTest.kt",
+        "tests/src/**/systemui/theme/ThemeOverlayApplierTest.java",
+        "tests/src/**/systemui/touch/TouchInsetManagerTest.java",
+        "tests/src/**/systemui/util/LifecycleFragmentTest.java",
+        "tests/src/**/systemui/util/TestableAlertDialogTest.kt",
+        "tests/src/**/systemui/util/kotlin/PairwiseFlowTest",
+        "tests/src/**/systemui/util/sensors/AsyncManagerTest.java",
+        "tests/src/**/systemui/util/sensors/ThresholdSensorImplTest.java",
+        "tests/src/**/systemui/util/wakelock/KeepAwakeAnimationListenerTest.java",
+        "tests/src/**/systemui/volume/VolumeDialogImplTest.java",
+        "tests/src/**/systemui/wallet/controller/QuickAccessWalletControllerTest.java",
+        "tests/src/**/systemui/wallet/ui/WalletScreenControllerTest.java",
     ],
 }
 
diff --git a/packages/SystemUI/aconfig/accessibility.aconfig b/packages/SystemUI/aconfig/accessibility.aconfig
index 55edff6..d201071 100644
--- a/packages/SystemUI/aconfig/accessibility.aconfig
+++ b/packages/SystemUI/aconfig/accessibility.aconfig
@@ -81,3 +81,13 @@
       purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "hearing_devices_dialog_related_tools"
+    namespace: "accessibility"
+    description: "Shows the related tools for hearing devices dialog."
+    bug: "341648471"
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 780a099..2cb297a 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -33,6 +33,13 @@
 }
 
 flag {
+   name: "notification_row_content_binder_refactor"
+   namespace: "systemui"
+   description: "Convert the NotificationContentInflater to Kotlin and restructure it to support modern views"
+   bug: "343942780"
+}
+
+flag {
    name: "notification_minimalism_prototype"
    namespace: "systemui"
    description: "Prototype of notification minimalism; the new 'Intermediate' lockscreen customization proposal."
@@ -177,7 +184,7 @@
 }
 
 flag {
-    name: "notification_throttle_hun"
+    name: "notification_avalanche_throttle_hun"
     namespace: "systemui"
     description: "During notification avalanche, throttle HUNs showing in fast succession."
     bug: "307288824"
@@ -407,6 +414,13 @@
 }
 
 flag {
+   name: "clock_reactive_variants"
+   namespace: "systemui"
+   description: "Add reactive variant fonts to some clocks"
+   bug: "343495953"
+}
+
+flag {
    name: "fast_unlock_transition"
    namespace: "systemui"
    description: "Faster wallpaper unlock transition"
@@ -736,16 +750,6 @@
 }
 
 flag {
-    name: "trim_resources_with_background_trim_at_lock"
-    namespace: "systemui"
-    description: "Trim fonts and other caches when the device locks to lower memory consumption."
-    bug: "322143614"
-    metadata {
-        purpose: PURPOSE_BUGFIX
-    }
-}
-
-flag {
     name: "dedicated_notif_inflation_thread"
     namespace: "systemui"
     description: "Create a separate background thread for inflating notifications"
@@ -960,7 +964,7 @@
 }
 
 flag {
-  name: "media_controls_user_initiated_dismiss"
+  name: "media_controls_user_initiated_deleteintent"
   namespace: "systemui"
   description: "Only dismiss media notifications when the control was removed by the user."
   bug: "335875159"
@@ -990,6 +994,20 @@
 }
 
 flag {
+  name: "glanceable_hub_fullscreen_swipe"
+  namespace: "systemui"
+  description: "Increase swipe area for gestures to bring in glanceable hub"
+  bug: "339665673"
+}
+
+flag {
+  name: "glanceable_hub_shortcut_button"
+  namespace: "systemui"
+  description: "Shows a button over the dream and lock screen to open the glanceable hub"
+  bug: "339667383"
+}
+
+flag {
   name: "glanceable_hub_gesture_handle"
   namespace: "systemui"
   description: "Shows a vertical bar at the right edge to indicate the user can swipe to open the glanceable hub"
@@ -997,6 +1015,13 @@
 }
 
 flag {
+  name: "glanceable_hub_allow_keyguard_when_dreaming"
+  namespace: "systemui"
+  description: "Allows users to exit dream to keyguard with glanceable hub enabled"
+  bug: "343505271"
+}
+
+flag {
   name: "new_touchpad_gestures_tutorial"
   namespace: "systemui"
   description: "Enables new interactive tutorial for learning touchpad gestures"
@@ -1012,3 +1037,13 @@
      purpose: PURPOSE_BUGFIX
    }
 }
+
+flag {
+  name: "notification_media_manager_background_execution"
+  namespace: "systemui"
+  description: "Decide whether to execute binder calls in background thread"
+  bug: "336612071"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
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 b124025..978943ae 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
@@ -179,8 +179,15 @@
 
     private val fontVariationUtils = FontVariationUtils()
 
-    fun updateLayout(layout: Layout) {
+    fun updateLayout(layout: Layout, textSize: Float = -1f) {
         textInterpolator.layout = layout
+
+        if (textSize >= 0) {
+            textInterpolator.targetPaint.textSize = textSize
+            textInterpolator.basePaint.textSize = textSize
+            textInterpolator.onTargetPaintModified()
+            textInterpolator.onBasePaintModified()
+        }
     }
 
     fun isRunning(): Boolean {
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxConfig.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxConfig.kt
new file mode 100644
index 0000000..72f0e86
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxConfig.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.surfaceeffects.glowboxeffect
+
+/** Parameters used to play [GlowBoxEffect]. */
+data class GlowBoxConfig(
+    /** Start center position X in px. */
+    val startCenterX: Float,
+    /** Start center position Y in px. */
+    val startCenterY: Float,
+    /** End center position X in px. */
+    val endCenterX: Float,
+    /** End center position Y in px. */
+    val endCenterY: Float,
+    /** Width of the box in px. */
+    val width: Float,
+    /** Height of the box in px. */
+    val height: Float,
+    /** Color of the box in ARGB, Apply alpha value if needed. */
+    val color: Int,
+    /** Amount of blur (or glow) of the box. */
+    val blurAmount: Float,
+    /**
+     * Duration of the animation. Note that the full duration of the animation is
+     * [duration] + [easeInDuration] + [easeOutDuration].
+     */
+    val duration: Long,
+    /** Ease in duration of the animation. */
+    val easeInDuration: Long,
+    /** Ease out duration of the animation. */
+    val easeOutDuration: Long,
+)
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxEffect.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxEffect.kt
new file mode 100644
index 0000000..5e590c1
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxEffect.kt
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.surfaceeffects.glowboxeffect
+
+import android.animation.ValueAnimator
+import android.graphics.Paint
+import androidx.annotation.VisibleForTesting
+import androidx.core.animation.doOnEnd
+import com.android.systemui.surfaceeffects.PaintDrawCallback
+import com.android.systemui.surfaceeffects.utils.MathUtils.lerp
+
+/** Glow box effect where the box moves from start to end positions defined in the [config]. */
+class GlowBoxEffect(
+    private var config: GlowBoxConfig,
+    private val paintDrawCallback: PaintDrawCallback,
+    private val stateChangedCallback: AnimationStateChangedCallback? = null
+) {
+    private val glowBoxShader =
+        GlowBoxShader().apply {
+            setSize(config.width, config.height)
+            setCenter(config.startCenterX, config.startCenterY)
+            setBlur(config.blurAmount)
+            setColor(config.color)
+        }
+    private var animator: ValueAnimator? = null
+    @VisibleForTesting var state: AnimationState = AnimationState.NOT_PLAYING
+    private val paint = Paint().apply { shader = glowBoxShader }
+
+    fun updateConfig(newConfig: GlowBoxConfig) {
+        this.config = newConfig
+
+        with(glowBoxShader) {
+            setSize(config.width, config.height)
+            setCenter(config.startCenterX, config.startCenterY)
+            setBlur(config.blurAmount)
+            setColor(config.color)
+        }
+    }
+
+    fun play() {
+        if (state != AnimationState.NOT_PLAYING) {
+            return
+        }
+
+        playEaseIn()
+    }
+
+    /** Finishes the animation with ease out. */
+    fun finish(force: Boolean = false) {
+        // If it's playing ease out, cancel immediately.
+        if (force && state == AnimationState.EASE_OUT) {
+            animator?.cancel()
+            return
+        }
+
+        // If it's playing either ease in or main, fast-forward to ease out.
+        if (state == AnimationState.EASE_IN || state == AnimationState.MAIN) {
+            animator?.pause()
+            playEaseOut()
+        }
+
+        // At this point, animation state should be ease out. Cancel it if force is true.
+        if (force) {
+            animator?.cancel()
+        }
+    }
+
+    private fun playEaseIn() {
+        if (state == AnimationState.EASE_IN) {
+            return
+        }
+        state = AnimationState.EASE_IN
+        stateChangedCallback?.onStart()
+
+        animator =
+            ValueAnimator.ofFloat(0f, 1f).apply {
+                duration = config.easeInDuration
+                addUpdateListener {
+                    val progress = it.animatedValue as Float
+                    glowBoxShader.setCenter(
+                        lerp(config.startCenterX, config.endCenterX, progress),
+                        lerp(config.startCenterY, config.endCenterY, progress)
+                    )
+
+                    draw()
+                }
+
+                doOnEnd {
+                    animator = null
+                    playMain()
+                }
+
+                start()
+            }
+    }
+
+    private fun playMain() {
+        if (state == AnimationState.MAIN) {
+            return
+        }
+        state = AnimationState.MAIN
+
+        animator =
+            ValueAnimator.ofFloat(0f, 1f).apply {
+                duration = config.duration
+                addUpdateListener { draw() }
+
+                doOnEnd {
+                    animator = null
+                    playEaseOut()
+                }
+
+                start()
+            }
+    }
+
+    private fun playEaseOut() {
+        if (state == AnimationState.EASE_OUT) return
+        state = AnimationState.EASE_OUT
+
+        animator =
+            ValueAnimator.ofFloat(0f, 1f).apply {
+                duration = config.easeOutDuration
+                addUpdateListener {
+                    val progress = it.animatedValue as Float
+                    glowBoxShader.setCenter(
+                        lerp(config.endCenterX, config.startCenterX, progress),
+                        lerp(config.endCenterY, config.startCenterY, progress)
+                    )
+
+                    draw()
+                }
+
+                doOnEnd {
+                    animator = null
+                    state = AnimationState.NOT_PLAYING
+                    stateChangedCallback?.onEnd()
+                }
+
+                start()
+            }
+    }
+
+    private fun draw() {
+        paintDrawCallback.onDraw(paint)
+    }
+
+    /**
+     * The animation state of the effect. The animation state transitions as follows: [EASE_IN] ->
+     * [MAIN] -> [EASE_OUT] -> [NOT_PLAYING].
+     */
+    enum class AnimationState {
+        EASE_IN,
+        MAIN,
+        EASE_OUT,
+        NOT_PLAYING,
+    }
+
+    interface AnimationStateChangedCallback {
+        /**
+         * Triggered when the animation starts, specifically when the states goes from
+         * [AnimationState.NOT_PLAYING] to [AnimationState.EASE_IN].
+         */
+        fun onStart()
+        /**
+         * Triggered when the animation ends, specifically when the states goes from
+         * [AnimationState.EASE_OUT] to [AnimationState.NOT_PLAYING].
+         */
+        fun onEnd()
+    }
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxShader.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxShader.kt
new file mode 100644
index 0000000..3693408
--- /dev/null
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxShader.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.surfaceeffects.glowboxeffect
+
+import android.graphics.RuntimeShader
+import com.android.systemui.surfaceeffects.shaderutil.SdfShaderLibrary
+
+/** Soft box shader. */
+class GlowBoxShader : RuntimeShader(GLOW_SHADER) {
+    // language=AGSL
+    private companion object {
+        private const val SHADER =
+            """
+            uniform half2 in_center;
+            uniform half2 in_size;
+            uniform half in_blur;
+            layout(color) uniform half4 in_color;
+
+            float4 main(float2 fragcoord) {
+                half glow = soften(sdBox(fragcoord - in_center, in_size), in_blur);
+                return in_color * (1. - glow);
+            }
+        """
+
+        private const val GLOW_SHADER =
+            SdfShaderLibrary.BOX_SDF + SdfShaderLibrary.SHADER_SDF_OPERATION_LIB + SHADER
+    }
+
+    fun setCenter(x: Float, y: Float) {
+        setFloatUniform("in_center", x, y)
+    }
+
+    fun setSize(width: Float, height: Float) {
+        setFloatUniform("in_size", width, height)
+    }
+
+    fun setBlur(blurAmount: Float) {
+        setFloatUniform("in_blur", blurAmount)
+    }
+
+    fun setColor(color: Int) {
+        setColorUniform("in_color", color)
+    }
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt
index 7889893..4efab58 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/shaderutil/SdfShaderLibrary.kt
@@ -35,17 +35,26 @@
             }
         """
 
+        const val BOX_SDF =
+            """
+                float sdBox(vec2 p, vec2 size) {
+                    size = size * 0.5;
+                    vec2 d = abs(p) - size;
+                    return length(max(d, 0.)) + min(max(d.x, d.y), 0.) / size.y;
+                }
+            """
+
         const val ROUNDED_BOX_SDF =
             """
             float sdRoundedBox(vec2 p, vec2 size, float cornerRadius) {
                 size *= 0.5;
                 cornerRadius *= 0.5;
-                vec2 d = abs(p)-size+cornerRadius;
+                vec2 d = abs(p) - size + cornerRadius;
 
                 float outside = length(max(d, 0.0));
                 float inside = min(max(d.x, d.y), 0.0);
 
-                return (outside+inside-cornerRadius)/size.y;
+                return (outside + inside - cornerRadius) / size.y;
             }
 
             float roundedBoxRing(vec2 p, vec2 size, float cornerRadius,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/utils/MathUtils.kt
similarity index 72%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt
copy to packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/utils/MathUtils.kt
index 277dbb7..7ed3b87 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/utils/MathUtils.kt
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.surfaceeffects.utils
 
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.iconLabelVisibilityRepository by Kosmos.Fixture { IconLabelVisibilityRepository() }
+/** Copied from android.utils.MathUtils */
+object MathUtils {
+    fun lerp(start: Float, stop: Float, amount: Float): Float {
+        return start + (stop - start) * amount
+    }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/ui/platform/DensityAwareComposeView.kt b/packages/SystemUI/compose/core/src/com/android/compose/ui/platform/DensityAwareComposeView.kt
deleted file mode 100644
index dff8753..0000000
--- a/packages/SystemUI/compose/core/src/com/android/compose/ui/platform/DensityAwareComposeView.kt
+++ /dev/null
@@ -1,96 +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.compose.ui.platform
-
-import android.content.Context
-import android.content.res.Configuration
-import android.util.AttributeSet
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.ui.platform.AbstractComposeView
-
-/**
- * A ComposeView that recreates its composition if the display size or font scale was changed.
- *
- * TODO(b/317317814): Remove this workaround.
- */
-class DensityAwareComposeView(context: Context) : OpenComposeView(context) {
-    private var lastDensityDpi: Int = -1
-    private var lastFontScale: Float = -1f
-
-    override fun onAttachedToWindow() {
-        super.onAttachedToWindow()
-
-        val configuration = context.resources.configuration
-        lastDensityDpi = configuration.densityDpi
-        lastFontScale = configuration.fontScale
-    }
-
-    override fun dispatchConfigurationChanged(newConfig: Configuration) {
-        super.dispatchConfigurationChanged(newConfig)
-
-        // If the density or font scale changed, we dispose then recreate the composition. Note that
-        // we do this here after dispatching the new configuration to children (instead of doing
-        // this in onConfigurationChanged()) because the new configuration should first be
-        // dispatched to the AndroidComposeView that holds the current density before we recreate
-        // the composition.
-        val densityDpi = newConfig.densityDpi
-        val fontScale = newConfig.fontScale
-        if (densityDpi != lastDensityDpi || fontScale != lastFontScale) {
-            lastDensityDpi = densityDpi
-            lastFontScale = fontScale
-
-            disposeComposition()
-            if (isAttachedToWindow) {
-                createComposition()
-            }
-        }
-    }
-}
-
-/** A fork of [androidx.compose.ui.platform.ComposeView] that is open and can be subclassed. */
-open class OpenComposeView
-internal constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
-    AbstractComposeView(context, attrs, defStyleAttr) {
-
-    private val content = mutableStateOf<(@Composable () -> Unit)?>(null)
-
-    @Suppress("RedundantVisibilityModifier")
-    protected override var shouldCreateCompositionOnAttachedToWindow: Boolean = false
-
-    @Composable
-    override fun Content() {
-        content.value?.invoke()
-    }
-
-    override fun getAccessibilityClassName(): CharSequence {
-        return javaClass.name
-    }
-
-    /**
-     * Set the Jetpack Compose UI content for this view. Initial composition will occur when the
-     * view becomes attached to a window or when [createComposition] is called, whichever comes
-     * first.
-     */
-    fun setContent(content: @Composable () -> Unit) {
-        shouldCreateCompositionOnAttachedToWindow = true
-        this.content.value = content
-        if (isAttachedToWindow) {
-            createComposition()
-        }
-    }
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
index c19c08e..b8f9ca8 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
@@ -66,6 +66,7 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.asImageBitmap
 import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.input.key.onKeyEvent
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.platform.LocalDensity
@@ -137,7 +138,7 @@
         // Despite the keyboard only being part of the password bouncer, adding it at this level is
         // both necessary to properly handle the keyboard in all layouts and harmless in cases when
         // the keyboard isn't used (like the PIN or pattern auth methods).
-        modifier = modifier.imePadding(),
+        modifier = modifier.imePadding().onKeyEvent(viewModel::onKeyEvent),
     ) {
         when (layout) {
             BouncerSceneLayout.STANDARD_BOUNCER ->
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 feb1f5b..c329384 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
@@ -1,9 +1,16 @@
 package com.android.systemui.communal.ui.compose
 
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.RepeatMode
+import androidx.compose.animation.core.animateFloat
+import androidx.compose.animation.core.infiniteRepeatable
+import androidx.compose.animation.core.rememberInfiniteTransition
 import androidx.compose.animation.core.tween
 import androidx.compose.foundation.background
+import androidx.compose.foundation.isSystemInDarkTheme
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
@@ -13,14 +20,24 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Brush
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.onGloballyPositioned
 import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.compose.animation.scene.CommunalSwipeDetector
+import com.android.compose.animation.scene.DefaultSwipeDetector
 import com.android.compose.animation.scene.Edge
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.ElementMatcher
@@ -34,7 +51,10 @@
 import com.android.compose.animation.scene.SwipeDirection
 import com.android.compose.animation.scene.observableTransitionState
 import com.android.compose.animation.scene.transitions
+import com.android.compose.theme.LocalAndroidColorScheme
 import com.android.systemui.Flags
+import com.android.systemui.Flags.glanceableHubFullscreenSwipe
+import com.android.systemui.communal.shared.model.CommunalBackgroundType
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.CommunalTransitionKeys
 import com.android.systemui.communal.ui.compose.extensions.allowGestures
@@ -99,6 +119,10 @@
     val touchesAllowed by viewModel.touchesAllowed.collectAsStateWithLifecycle(initialValue = false)
     val showGestureIndicator by
         viewModel.showGestureIndicator.collectAsStateWithLifecycle(initialValue = false)
+    val backgroundType by
+        viewModel.communalBackground.collectAsStateWithLifecycle(
+            initialValue = CommunalBackgroundType.DEFAULT
+        )
     val state: MutableSceneTransitionLayoutState = remember {
         MutableSceneTransitionLayoutState(
             initialScene = currentSceneKey,
@@ -108,6 +132,8 @@
         )
     }
 
+    val detector = remember { CommunalSwipeDetector() }
+
     DisposableEffect(state) {
         val dataSource = SceneTransitionLayoutDataSource(state, coroutineScope)
         dataSourceDelegator.setDelegate(dataSource)
@@ -121,13 +147,25 @@
         onDispose { viewModel.setTransitionState(null) }
     }
 
+    val swipeSourceDetector =
+        if (glanceableHubFullscreenSwipe()) {
+            detector
+        } else {
+            FixedSizeEdgeDetector(dimensionResource(id = R.dimen.communal_gesture_initiation_width))
+        }
+
+    val swipeDetector =
+        if (glanceableHubFullscreenSwipe()) {
+            detector
+        } else {
+            DefaultSwipeDetector
+        }
+
     SceneTransitionLayout(
         state = state,
         modifier = modifier.fillMaxSize(),
-        swipeSourceDetector =
-            FixedSizeEdgeDetector(
-                dimensionResource(id = R.dimen.communal_gesture_initiation_width)
-            ),
+        swipeSourceDetector = swipeSourceDetector,
+        swipeDetector = swipeDetector,
     ) {
         scene(
             CommunalScenes.Blank,
@@ -157,7 +195,7 @@
             userActions =
                 mapOf(Swipe(SwipeDirection.Right, fromSource = Edge.Left) to CommunalScenes.Blank)
         ) {
-            CommunalScene(colors, content)
+            CommunalScene(backgroundType, colors, content)
         }
     }
 
@@ -169,17 +207,87 @@
 /** Scene containing the glanceable hub UI. */
 @Composable
 private fun SceneScope.CommunalScene(
+    backgroundType: CommunalBackgroundType,
     colors: CommunalColors,
     content: CommunalContent,
     modifier: Modifier = Modifier,
 ) {
-    val backgroundColor by colors.backgroundColor.collectAsStateWithLifecycle()
-
-    Box(
-        modifier =
-            Modifier.element(Communal.Elements.Scrim)
-                .fillMaxSize()
-                .background(Color(backgroundColor.toArgb())),
-    )
+    Box(modifier = Modifier.element(Communal.Elements.Scrim).fillMaxSize()) {
+        when (backgroundType) {
+            CommunalBackgroundType.DEFAULT -> DefaultBackground(colors = colors)
+            CommunalBackgroundType.STATIC_GRADIENT -> StaticLinearGradient()
+            CommunalBackgroundType.ANIMATED -> AnimatedLinearGradient()
+        }
+    }
     with(content) { Content(modifier = modifier) }
 }
+
+/** Default background of the hub, a single color */
+@Composable
+private fun BoxScope.DefaultBackground(
+    colors: CommunalColors,
+) {
+    val backgroundColor by colors.backgroundColor.collectAsStateWithLifecycle()
+    Box(
+        modifier = Modifier.matchParentSize().background(Color(backgroundColor.toArgb())),
+    )
+}
+
+/** Experimental hub background, static linear gradient */
+@Composable
+private fun BoxScope.StaticLinearGradient() {
+    val colors = LocalAndroidColorScheme.current
+    Box(
+        Modifier.matchParentSize()
+            .background(
+                Brush.linearGradient(colors = listOf(colors.primary, colors.primaryContainer)),
+            )
+    )
+    BackgroundTopScrim()
+}
+
+/** Experimental hub background, animated linear gradient */
+@Composable
+private fun BoxScope.AnimatedLinearGradient() {
+    val colors = LocalAndroidColorScheme.current
+    Box(
+        Modifier.matchParentSize()
+            .animatedGradientBackground(colors = listOf(colors.primary, colors.primaryContainer))
+    )
+    BackgroundTopScrim()
+}
+
+/** Scrim placed on top of the background in order to dim/bright colors */
+@Composable
+private fun BoxScope.BackgroundTopScrim() {
+    val darkTheme = isSystemInDarkTheme()
+    val scrimOnTopColor = if (darkTheme) Color.Black else Color.White
+    Box(Modifier.matchParentSize().alpha(0.34f).background(scrimOnTopColor))
+}
+
+/** Modifier which sets the background of a composable to an animated gradient */
+@Composable
+private fun Modifier.animatedGradientBackground(colors: List<Color>): Modifier = composed {
+    var size by remember { mutableStateOf(IntSize.Zero) }
+    val transition = rememberInfiniteTransition(label = "scrim background")
+    val startOffsetX by
+        transition.animateFloat(
+            initialValue = -size.width.toFloat(),
+            targetValue = size.width.toFloat(),
+            animationSpec =
+                infiniteRepeatable(
+                    animation = tween(durationMillis = 5_000, easing = LinearEasing),
+                    repeatMode = RepeatMode.Reverse,
+                ),
+            label = "scrim start offset"
+        )
+    background(
+            brush =
+                Brush.linearGradient(
+                    colors = colors,
+                    start = Offset(startOffsetX, 0f),
+                    end = Offset(startOffsetX + size.width.toFloat(), size.height.toFloat()),
+                )
+        )
+        .onGloballyPositioned { size = it.size }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index cd27d57..1f7f07b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -23,11 +23,14 @@
 import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_AUTO
 import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
 import android.widget.FrameLayout
+import androidx.annotation.VisibleForTesting
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.animation.AnimatedVisibilityScope
 import androidx.compose.animation.ExperimentalAnimationApi
 import androidx.compose.animation.core.LinearEasing
+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.animation.fadeIn
 import androidx.compose.animation.fadeOut
@@ -162,7 +165,6 @@
     var removeButtonCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
     var toolbarSize: IntSize? by remember { mutableStateOf(null) }
     var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
-    var isDraggingToRemove by remember { mutableStateOf(false) }
     val gridState = rememberLazyGridState()
     val contentListState = rememberContentListState(widgetConfigurator, communalContent, viewModel)
     val reorderingWidgets by viewModel.reorderingWidgets.collectAsStateWithLifecycle()
@@ -250,12 +252,11 @@
                     contentOffset = contentOffset,
                     setGridCoordinates = { gridCoordinates = it },
                     updateDragPositionForRemove = { offset ->
-                        isDraggingToRemove =
-                            isPointerWithinCoordinates(
-                                offset = gridCoordinates?.let { it.positionInWindow() + offset },
-                                containerToCheck = removeButtonCoordinates
-                            )
-                        isDraggingToRemove
+                        isPointerWithinEnabledRemoveButton(
+                            removeEnabled = removeButtonEnabled,
+                            offset = gridCoordinates?.let { it.positionInWindow() + offset },
+                            containerToCheck = removeButtonCoordinates
+                        )
                     },
                     gridState = gridState,
                     contentListState = contentListState,
@@ -446,6 +447,14 @@
                 val selected by
                     remember(index) { derivedStateOf { list[index].key == selectedKey.value } }
                 DraggableItem(
+                    modifier =
+                        if (dragDropState.draggingItemIndex == index) {
+                            Modifier
+                        } else {
+                            Modifier.animateItem(
+                                placementSpec = spring(stiffness = Spring.StiffnessMediumLow)
+                            )
+                        },
                     dragDropState = dragDropState,
                     selected = selected,
                     enabled = list[index].isWidgetContent(),
@@ -640,11 +649,16 @@
                         enter =
                             fadeIn(
                                 initialAlpha = 0f,
-                                animationSpec = tween(durationMillis = 500, easing = LinearEasing)
+                                animationSpec = tween(durationMillis = 83, easing = LinearEasing)
                             ),
                         exit =
                             fadeOut(
-                                animationSpec = tween(durationMillis = 500, easing = LinearEasing)
+                                animationSpec =
+                                    tween(
+                                        durationMillis = 83,
+                                        delayMillis = 167,
+                                        easing = LinearEasing
+                                    )
                             )
                     )
                     .background(colors.secondary, RoundedCornerShape(50.dp)),
@@ -658,7 +672,7 @@
                                 animationSpec =
                                     tween(
                                         durationMillis = 167,
-                                        delayMillis = 500,
+                                        delayMillis = 83,
                                         easing = LinearEasing
                                     )
                             ),
@@ -1195,11 +1209,13 @@
  * Check whether the pointer position that the item is being dragged at is within the coordinates of
  * the remove button in the toolbar. Returns true if the item is removable.
  */
-private fun isPointerWithinCoordinates(
+@VisibleForTesting
+fun isPointerWithinEnabledRemoveButton(
+    removeEnabled: Boolean,
     offset: Offset?,
     containerToCheck: LayoutCoordinates?
 ): Boolean {
-    if (offset == null || containerToCheck == null) {
+    if (!removeEnabled || offset == null || containerToCheck == null) {
         return false
     }
     val container = containerToCheck.boundsInWindow()
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationHeadsUpHeight.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationHeadsUpHeight.kt
new file mode 100644
index 0000000..75a565b
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationHeadsUpHeight.kt
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.notifications.ui.composable
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.node.invalidateMeasurement
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.IntOffset
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
+
+/**
+ * Modify element, which updates the height to the height of current top heads up notification, or
+ * to 0 if there is none.
+ *
+ * @param view Notification stack scroll view
+ */
+fun Modifier.notificationHeadsUpHeight(view: NotificationScrollView) =
+    this then HeadsUpLayoutElement(view)
+
+private data class HeadsUpLayoutElement(
+    val view: NotificationScrollView,
+) : ModifierNodeElement<HeadsUpLayoutNode>() {
+
+    override fun create(): HeadsUpLayoutNode = HeadsUpLayoutNode(view)
+
+    override fun update(node: HeadsUpLayoutNode) {
+        check(view == node.view) { "Trying to reuse the node with a new View." }
+    }
+}
+
+private class HeadsUpLayoutNode(val view: NotificationScrollView) :
+    LayoutModifierNode, Modifier.Node() {
+
+    private val headsUpHeightChangedListener = Runnable { invalidateMeasureIfAttached() }
+
+    override fun onAttach() {
+        super.onAttach()
+        view.addHeadsUpHeightChangedListener(headsUpHeightChangedListener)
+    }
+
+    override fun onDetach() {
+        super.onDetach()
+        view.removeHeadsUpHeightChangedListener(headsUpHeightChangedListener)
+    }
+
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints
+    ): MeasureResult {
+        // TODO(b/339181697) make sure, that the row is already measured.
+        val contentHeight = view.topHeadsUpHeight
+        val placeable =
+            measurable.measure(
+                constraints.copy(minHeight = contentHeight, maxHeight = contentHeight)
+            )
+        return layout(placeable.width, placeable.height) { placeable.place(IntOffset.Zero) }
+    }
+
+    override fun toString(): String {
+        return "HeadsUpLayoutNode(view=$view)"
+    }
+
+    fun invalidateMeasureIfAttached() {
+        if (isAttached) {
+            this.invalidateMeasurement()
+        }
+    }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index e6132c6..27a834b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -67,7 +67,6 @@
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.NestedScrollBehavior
 import com.android.compose.animation.scene.SceneScope
-import com.android.compose.modifiers.height
 import com.android.compose.modifiers.thenIf
 import com.android.systemui.common.ui.compose.windowinsets.LocalRawScreenHeight
 import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius
@@ -108,18 +107,17 @@
  */
 @Composable
 fun SceneScope.HeadsUpNotificationSpace(
+    stackScrollView: NotificationScrollView,
     viewModel: NotificationsPlaceholderViewModel,
     modifier: Modifier = Modifier,
     isPeekFromBottom: Boolean = false,
 ) {
-    val headsUpHeight = viewModel.headsUpHeight.collectAsStateWithLifecycle()
-
     Element(
         Notifications.Elements.HeadsUpNotificationPlaceholder,
         modifier =
             modifier
-                .height { headsUpHeight.value.roundToInt() }
                 .fillMaxWidth()
+                .notificationHeadsUpHeight(stackScrollView)
                 .debugBackground(viewModel, DEBUG_HUN_COLOR)
                 .onGloballyPositioned { coordinates: LayoutCoordinates ->
                     val boundsInWindow = coordinates.boundsInWindow()
@@ -128,7 +126,8 @@
                             " size=${coordinates.size}" +
                             " bounds=$boundsInWindow"
                     }
-                    viewModel.onHeadsUpTopChanged(boundsInWindow.top)
+                    // Note: boundsInWindow doesn't scroll off the screen
+                    stackScrollView.setHeadsUpTop(boundsInWindow.top)
                 }
     ) {
         content {}
@@ -152,6 +151,7 @@
             modifier = Modifier.fillMaxSize(),
         )
         HeadsUpNotificationSpace(
+            stackScrollView = stackScrollView,
             viewModel = viewModel,
             modifier = Modifier.align(Alignment.TopCenter),
         )
@@ -170,6 +170,7 @@
     maxScrimTop: () -> Float,
     shouldPunchHoleBehindScrim: Boolean,
     shouldFillMaxSize: Boolean = true,
+    shouldReserveSpaceForNavBar: Boolean = true,
     shadeMode: ShadeMode,
     modifier: Modifier = Modifier,
 ) {
@@ -353,12 +354,12 @@
                         .fillMaxWidth()
                         .notificationStackHeight(
                             view = stackScrollView,
-                            padding = navBarHeight.toInt()
+                            padding = if (shouldReserveSpaceForNavBar) navBarHeight.toInt() else 0
                         )
                         .onSizeChanged { size -> stackHeight.intValue = size.height },
             )
         }
-        HeadsUpNotificationSpace(viewModel = viewModel)
+        HeadsUpNotificationSpace(stackScrollView = stackScrollView, viewModel = viewModel)
     }
 }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
index ae53d56..edb1727 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationsShadeScene.kt
@@ -18,8 +18,8 @@
 
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
@@ -75,7 +75,7 @@
         OverlayShade(
             modifier = modifier,
             viewModel = overlayShadeViewModel,
-            horizontalArrangement = Arrangement.Start,
+            horizontalArrangement = Arrangement.End,
             lockscreenContent = lockscreenContent,
         ) {
             Column {
@@ -94,8 +94,9 @@
                     maxScrimTop = { 0f },
                     shouldPunchHoleBehindScrim = false,
                     shouldFillMaxSize = false,
+                    shouldReserveSpaceForNavBar = false,
                     shadeMode = ShadeMode.Dual,
-                    modifier = Modifier.width(416.dp),
+                    modifier = Modifier.fillMaxWidth(),
                 )
             }
         }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
index 54a98dd..8195df3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettings.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.qs.ui.composable
 
+import android.view.ViewGroup
+import android.widget.FrameLayout
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxHeight
 import androidx.compose.foundation.layout.fillMaxWidth
@@ -24,6 +26,7 @@
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawWithContent
 import androidx.compose.ui.layout.layout
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.viewinterop.AndroidView
@@ -164,11 +167,8 @@
     state: () -> QSSceneAdapter.State,
     modifier: Modifier = Modifier,
 ) {
-    val qsView by qsSceneAdapter.qsView.collectAsStateWithLifecycle(null)
-    val isCustomizing by
-        qsSceneAdapter.isCustomizerShowing.collectAsStateWithLifecycle(
-            qsSceneAdapter.isCustomizerShowing.value
-        )
+    val qsView by qsSceneAdapter.qsView.collectAsStateWithLifecycle()
+    val isCustomizing by qsSceneAdapter.isCustomizerShowing.collectAsStateWithLifecycle()
     QuickSettingsTheme {
         val context = LocalContext.current
 
@@ -180,15 +180,34 @@
         qsView?.let { view ->
             Box(
                 modifier =
-                    modifier.fillMaxWidth().thenIf(isCustomizing) { Modifier.fillMaxHeight() }
+                    modifier
+                        .fillMaxWidth()
+                        .thenIf(isCustomizing) { Modifier.fillMaxHeight() }
+                        .drawWithContent {
+                            qsSceneAdapter.applyLatestExpansionAndSquishiness()
+                            drawContent()
+                        }
             ) {
                 AndroidView(
                     modifier = Modifier.fillMaxWidth(),
-                    factory = { _ ->
+                    factory = { context ->
                         qsSceneAdapter.setState(state())
-                        view
+                        FrameLayout(context).apply {
+                            (view.parent as? ViewGroup)?.removeView(view)
+                            addView(view)
+                        }
                     },
-                    update = { qsSceneAdapter.setState(state()) }
+                    // When the view changes (e.g. due to a theme change), this will be recomposed
+                    // if needed and the new view will be attached to the FrameLayout here.
+                    update = {
+                        qsSceneAdapter.setState(state())
+                        if (view.parent != it) {
+                            it.removeAllViews()
+                            (view.parent as? ViewGroup)?.removeView(view)
+                            it.addView(view)
+                        }
+                    },
+                    onRelease = { it.removeAllViews() }
                 )
             }
         }
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 d76b19f..0ee485c 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
@@ -42,7 +42,6 @@
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.layout.navigationBars
-import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.wrapContentHeight
 import androidx.compose.foundation.rememberScrollState
@@ -60,7 +59,6 @@
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalLifecycleOwner
 import androidx.compose.ui.res.colorResource
-import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
@@ -79,14 +77,13 @@
 import com.android.systemui.media.controls.ui.controller.MediaCarouselController
 import com.android.systemui.media.controls.ui.view.MediaHost
 import com.android.systemui.media.dagger.MediaModule
-import com.android.systemui.notifications.ui.composable.NotificationScrollingStack
+import com.android.systemui.notifications.ui.composable.HeadsUpNotificationSpace
 import com.android.systemui.qs.footer.ui.compose.FooterActionsWithAnimatedVisibility
 import com.android.systemui.qs.ui.viewmodel.QuickSettingsSceneViewModel
 import com.android.systemui.res.R
 import com.android.systemui.scene.session.ui.composable.SaveableSession
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.ui.composable.ComposableScene
-import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.shade.ui.composable.CollapsedShadeHeader
 import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
 import com.android.systemui.shade.ui.composable.Shade
@@ -99,7 +96,6 @@
 import dagger.Lazy
 import javax.inject.Inject
 import javax.inject.Named
-import kotlin.math.roundToInt
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.stateIn
@@ -368,15 +364,11 @@
                     Modifier.align(Alignment.CenterHorizontally).sysuiResTag("qs_footer_actions"),
             )
         }
-        NotificationScrollingStack(
+        HeadsUpNotificationSpace(
             stackScrollView = notificationStackScrollView,
             viewModel = notificationsPlaceholderViewModel,
-            shadeSession = shadeSession,
-            maxScrimTop = { screenHeight },
-            shouldPunchHoleBehindScrim = shouldPunchHoleBehindScrim,
-            shadeMode = ShadeMode.Single,
-            modifier =
-                Modifier.fillMaxWidth().offset { IntOffset(x = 0, y = screenHeight.roundToInt()) },
+            modifier = Modifier.align(Alignment.BottomCenter),
+            isPeekFromBottom = true,
         )
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
index 4d946bf..4bf90ec 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsShadeScene.kt
@@ -16,22 +16,44 @@
 
 package com.android.systemui.qs.ui.composable
 
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.EnterTransition
+import androidx.compose.animation.ExitTransition
+import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
 import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.heightIn
 import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Button
 import androidx.compose.material3.Text
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.battery.BatteryMeterViewController
+import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.ui.composable.LockscreenContent
+import com.android.systemui.qs.panels.ui.compose.EditMode
+import com.android.systemui.qs.panels.ui.compose.TileGrid
 import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeSceneViewModel
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.ui.composable.ComposableScene
+import com.android.systemui.shade.ui.composable.ExpandedShadeHeader
 import com.android.systemui.shade.ui.composable.OverlayShade
-import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
+import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel
+import com.android.systemui.statusbar.phone.ui.StatusBarIconController
+import com.android.systemui.statusbar.phone.ui.TintedIconManager
 import dagger.Lazy
 import java.util.Optional
 import javax.inject.Inject
@@ -41,9 +63,12 @@
 class QuickSettingsShadeScene
 @Inject
 constructor(
-    viewModel: QuickSettingsShadeSceneViewModel,
-    private val overlayShadeViewModel: OverlayShadeViewModel,
+    private val viewModel: QuickSettingsShadeSceneViewModel,
     private val lockscreenContent: Lazy<Optional<LockscreenContent>>,
+    private val shadeHeaderViewModel: ShadeHeaderViewModel,
+    private val tintedIconManagerFactory: TintedIconManager.Factory,
+    private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory,
+    private val statusBarIconController: StatusBarIconController,
 ) : ComposableScene {
 
     override val key = Scenes.QuickSettingsShade
@@ -56,21 +81,101 @@
         modifier: Modifier,
     ) {
         OverlayShade(
-            viewModel = overlayShadeViewModel,
-            modifier = modifier,
+            viewModel = viewModel.overlayShadeViewModel,
             horizontalArrangement = Arrangement.End,
             lockscreenContent = lockscreenContent,
+            modifier = modifier,
         ) {
-            Text(
-                text = "Quick settings grid",
-                modifier = Modifier.padding(QuickSettingsShade.Dimensions.Padding)
+            Column {
+                ExpandedShadeHeader(
+                    viewModel = shadeHeaderViewModel,
+                    createTintedIconManager = tintedIconManagerFactory::create,
+                    createBatteryMeterViewController = batteryMeterViewControllerFactory::create,
+                    statusBarIconController = statusBarIconController,
+                    modifier = Modifier.padding(QuickSettingsShade.Dimensions.Padding),
+                )
+
+                ShadeBody(
+                    viewModel = viewModel,
+                )
+            }
+        }
+    }
+}
+
+@Composable
+private fun ShadeBody(
+    viewModel: QuickSettingsShadeSceneViewModel,
+) {
+    val isEditing by viewModel.editModeViewModel.isEditing.collectAsStateWithLifecycle()
+
+    Box {
+        // The main Quick Settings grid layout.
+        AnimatedVisibility(
+            visible = !isEditing,
+            enter = QuickSettingsShade.Transitions.QuickSettingsLayoutEnter,
+            exit = QuickSettingsShade.Transitions.QuickSettingsLayoutExit,
+        ) {
+            QuickSettingsLayout(
+                viewModel = viewModel,
+            )
+        }
+
+        // The Quick Settings Editor layout.
+        AnimatedVisibility(
+            visible = isEditing,
+            enter = QuickSettingsShade.Transitions.QuickSettingsEditorEnter,
+            exit = QuickSettingsShade.Transitions.QuickSettingsEditorExit,
+        ) {
+            EditMode(
+                viewModel = viewModel.editModeViewModel,
+                modifier = Modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding)
             )
         }
     }
 }
 
+@Composable
+private fun QuickSettingsLayout(
+    viewModel: QuickSettingsShadeSceneViewModel,
+    modifier: Modifier = Modifier,
+) {
+    Column(
+        verticalArrangement = Arrangement.spacedBy(QuickSettingsShade.Dimensions.Padding),
+        horizontalAlignment = Alignment.CenterHorizontally,
+        modifier = modifier.fillMaxWidth().padding(QuickSettingsShade.Dimensions.Padding),
+    ) {
+        BrightnessSliderContainer(
+            viewModel = viewModel.brightnessSliderViewModel,
+            modifier =
+                Modifier.fillMaxWidth()
+                    .height(QuickSettingsShade.Dimensions.BrightnessSliderHeight),
+        )
+        TileGrid(
+            viewModel = viewModel.tileGridViewModel,
+            modifier =
+                Modifier.fillMaxWidth().heightIn(max = QuickSettingsShade.Dimensions.GridMaxHeight),
+        )
+        Button(
+            onClick = { viewModel.editModeViewModel.startEditing() },
+        ) {
+            Text("Edit mode")
+        }
+    }
+}
+
 object QuickSettingsShade {
+
     object Dimensions {
         val Padding = 16.dp
+        val BrightnessSliderHeight = 64.dp
+        val GridMaxHeight = 400.dp
+    }
+
+    object Transitions {
+        val QuickSettingsLayoutEnter: EnterTransition = fadeIn(tween(500))
+        val QuickSettingsLayoutExit: ExitTransition = fadeOut(tween(500))
+        val QuickSettingsEditorEnter: EnterTransition = fadeIn(tween(500))
+        val QuickSettingsEditorExit: ExitTransition = fadeOut(tween(500))
     }
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index 975829a..efda4cd 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -17,17 +17,28 @@
 package com.android.systemui.scene.ui.composable
 
 import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.absoluteOffset
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.IntOffset
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
 import com.android.compose.animation.scene.animateSceneFloatAsState
+import com.android.internal.policy.SystemBarUtils
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.notifications.ui.composable.HeadsUpNotificationSpace
 import com.android.systemui.qs.ui.composable.QuickSettings
+import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.scene.ui.viewmodel.GoneSceneViewModel
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
+import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel
+import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.flow.StateFlow
 
@@ -39,6 +50,8 @@
 class GoneScene
 @Inject
 constructor(
+    private val notificationStackScrolLView: Lazy<NotificationScrollView>,
+    private val notificationsPlaceholderViewModel: NotificationsPlaceholderViewModel,
     private val viewModel: GoneSceneViewModel,
 ) : ComposableScene {
     override val key = Scenes.Gone
@@ -55,5 +68,28 @@
             key = QuickSettings.SharedValues.TilesSquishiness,
         )
         Spacer(modifier.fillMaxSize())
+        HeadsUpNotificationStack(
+            stackScrollView = notificationStackScrolLView.get(),
+            viewModel = notificationsPlaceholderViewModel
+        )
     }
 }
+
+@Composable
+private fun SceneScope.HeadsUpNotificationStack(
+    stackScrollView: NotificationScrollView,
+    viewModel: NotificationsPlaceholderViewModel,
+) {
+    val context = LocalContext.current
+    val density = LocalDensity.current
+    val statusBarHeight = SystemBarUtils.getStatusBarHeight(context)
+    val headsUpPadding =
+        with(density) { dimensionResource(id = R.dimen.heads_up_status_bar_padding).roundToPx() }
+
+    HeadsUpNotificationSpace(
+        stackScrollView = stackScrollView,
+        viewModel = viewModel,
+        modifier =
+            Modifier.absoluteOffset { IntOffset(x = 0, y = statusBarHeight + headsUpPadding) }
+    )
+}
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 f5a0ef2..10c4030 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
@@ -22,6 +22,7 @@
 import com.android.systemui.scene.ui.composable.transitions.lockscreenToShadeTransition
 import com.android.systemui.scene.ui.composable.transitions.lockscreenToSplitShadeTransition
 import com.android.systemui.scene.ui.composable.transitions.shadeToQuickSettingsTransition
+import com.android.systemui.shade.ui.composable.OverlayShade
 import com.android.systemui.shade.ui.composable.Shade
 
 /**
@@ -102,4 +103,10 @@
             y = { Shade.Dimensions.ScrimOverscrollLimit }
         )
     }
+    overscroll(Scenes.NotificationsShade, Orientation.Vertical) {
+        translate(OverlayShade.Elements.Panel, y = OverlayShade.Dimensions.OverscrollLimit)
+    }
+    overscroll(Scenes.QuickSettingsShade, Orientation.Vertical) {
+        translate(OverlayShade.Elements.Panel, y = OverlayShade.Dimensions.OverscrollLimit)
+    }
 }
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 a6b268d..6b3b760 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
@@ -50,8 +50,7 @@
             }
         }
 
-    translate(OverlayShade.Elements.PanelBackground, Edge.Top)
-    translate(Notifications.Elements.NotificationScrim, Edge.Top)
+    translate(OverlayShade.Elements.Panel, Edge.Top)
 
     fractionRange(end = .5f) { fade(OverlayShade.Elements.Scrim) }
 
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 2baaecf..ec2f14f 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
@@ -48,7 +48,7 @@
             }
         }
 
-    translate(OverlayShade.Elements.PanelBackground, Edge.Top)
+    translate(OverlayShade.Elements.Panel, Edge.Top)
 
     fractionRange(end = .5f) { fade(OverlayShade.Elements.Scrim) }
 }
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 34cc676..6924d43 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
@@ -14,17 +14,28 @@
  * limitations under the License.
  */
 
+@file:OptIn(ExperimentalLayoutApi::class)
+
 package com.android.systemui.shade.ui.composable
 
 import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.ExperimentalLayoutApi
+import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.asPaddingValues
+import androidx.compose.foundation.layout.calculateEndPadding
+import androidx.compose.foundation.layout.calculateStartPadding
+import androidx.compose.foundation.layout.displayCutout
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.systemBarsIgnoringVisibility
+import androidx.compose.foundation.layout.waterfall
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material3.MaterialTheme
@@ -35,12 +46,12 @@
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.draw.clip
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.LowestZIndexScenePicker
 import com.android.compose.animation.scene.SceneScope
-import com.android.compose.modifiers.thenIf
 import com.android.compose.windowsizeclass.LocalWindowSizeClass
 import com.android.systemui.keyguard.ui.composable.LockscreenContent
 import com.android.systemui.scene.shared.model.Scenes
@@ -59,8 +70,6 @@
     content: @Composable () -> Unit,
 ) {
     val backgroundScene by viewModel.backgroundScene.collectAsStateWithLifecycle()
-    val widthSizeClass = LocalWindowSizeClass.current.widthSizeClass
-    val isPanelFullWidth = widthSizeClass == WindowWidthSizeClass.Compact
 
     Box(modifier) {
         if (backgroundScene == Scenes.Lockscreen) {
@@ -73,13 +82,13 @@
         Scrim(onClicked = viewModel::onScrimClicked)
 
         Row(
-            modifier =
-                Modifier.fillMaxSize().thenIf(!isPanelFullWidth) {
-                    Modifier.padding(OverlayShade.Dimensions.ScrimContentPadding)
-                },
+            modifier = Modifier.fillMaxSize().panelPadding(),
             horizontalArrangement = horizontalArrangement,
         ) {
-            Panel(modifier = Modifier.panelSize(), content = content)
+            Panel(
+                modifier = Modifier.element(OverlayShade.Elements.Panel).panelSize(),
+                content = content
+            )
         }
     }
 }
@@ -135,9 +144,46 @@
     )
 }
 
+@Composable
+private fun Modifier.panelPadding(): Modifier {
+    val widthSizeClass = LocalWindowSizeClass.current.widthSizeClass
+    val systemBars = WindowInsets.systemBarsIgnoringVisibility
+    val displayCutout = WindowInsets.displayCutout
+    val waterfall = WindowInsets.waterfall
+    val contentPadding = PaddingValues(all = OverlayShade.Dimensions.ScrimContentPadding)
+
+    val combinedPadding =
+        combinePaddings(
+            systemBars.asPaddingValues(),
+            displayCutout.asPaddingValues(),
+            waterfall.asPaddingValues(),
+            contentPadding
+        )
+
+    return if (widthSizeClass == WindowWidthSizeClass.Compact) {
+        padding(bottom = combinedPadding.calculateBottomPadding())
+    } else {
+        padding(combinedPadding)
+    }
+}
+
+/** Creates a union of [paddingValues] by using the max padding of each edge. */
+@Composable
+private fun combinePaddings(vararg paddingValues: PaddingValues): PaddingValues {
+    val layoutDirection = LocalLayoutDirection.current
+
+    return PaddingValues(
+        start = paddingValues.maxOfOrNull { it.calculateStartPadding(layoutDirection) } ?: 0.dp,
+        top = paddingValues.maxOfOrNull { it.calculateTopPadding() } ?: 0.dp,
+        end = paddingValues.maxOfOrNull { it.calculateEndPadding(layoutDirection) } ?: 0.dp,
+        bottom = paddingValues.maxOfOrNull { it.calculateBottomPadding() } ?: 0.dp
+    )
+}
+
 object OverlayShade {
     object Elements {
         val Scrim = ElementKey("OverlayShadeScrim", scenePicker = LowestZIndexScenePicker)
+        val Panel = ElementKey("OverlayShadePanel", scenePicker = LowestZIndexScenePicker)
         val PanelBackground =
             ElementKey("OverlayShadePanelBackground", scenePicker = LowestZIndexScenePicker)
     }
@@ -153,6 +199,7 @@
         val PanelCornerRadius = 46.dp
         val PanelWidthMedium = 390.dp
         val PanelWidthLarge = 474.dp
+        val OverscrollLimit = 32.dp
     }
 
     object Shapes {
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 9d689fc..33a630c 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
@@ -25,6 +25,7 @@
 import androidx.compose.foundation.clipScrollableContainer
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
@@ -42,6 +43,7 @@
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
@@ -67,6 +69,7 @@
 import com.android.compose.animation.scene.animateSceneFloatAsState
 import com.android.compose.modifiers.padding
 import com.android.compose.modifiers.thenIf
+import com.android.compose.windowsizeclass.LocalWindowSizeClass
 import com.android.systemui.battery.BatteryMeterViewController
 import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation
 import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout
@@ -235,6 +238,10 @@
     val shouldPunchHoleBehindScrim =
         layoutState.isTransitioningBetween(Scenes.Gone, Scenes.Shade) ||
             layoutState.isTransitioningBetween(Scenes.Lockscreen, Scenes.Shade)
+    // Media is visible and we are in landscape on a small height screen
+    val mediaInRow =
+        isMediaVisible &&
+            LocalWindowSizeClass.current.heightSizeClass == WindowHeightSizeClass.Compact
 
     Box(
         modifier =
@@ -274,22 +281,39 @@
                                 createBatteryMeterViewController = createBatteryMeterViewController,
                                 statusBarIconController = statusBarIconController,
                             )
-                            Box(Modifier.element(QuickSettings.Elements.QuickQuickSettings)) {
-                                QuickSettings(
-                                    viewModel.qsSceneAdapter,
-                                    { viewModel.qsSceneAdapter.qqsHeight },
-                                    isSplitShade = false,
-                                    squishiness = { tileSquishiness },
+
+                            val content: @Composable (Modifier) -> Unit = { modifier ->
+                                Box(
+                                    Modifier.element(QuickSettings.Elements.QuickQuickSettings)
+                                        .then(modifier)
+                                ) {
+                                    QuickSettings(
+                                        viewModel.qsSceneAdapter,
+                                        { viewModel.qsSceneAdapter.qqsHeight },
+                                        isSplitShade = false,
+                                        squishiness = { tileSquishiness },
+                                    )
+                                }
+
+                                MediaCarousel(
+                                    isVisible = isMediaVisible,
+                                    mediaHost = mediaHost,
+                                    modifier = Modifier.fillMaxWidth().then(modifier),
+                                    carouselController = mediaCarouselController,
                                 )
                             }
 
-                            MediaCarousel(
-                                isVisible = isMediaVisible,
-                                mediaHost = mediaHost,
-                                modifier = Modifier.fillMaxWidth(),
-                                carouselController = mediaCarouselController,
-                            )
-
+                            if (!mediaInRow) {
+                                content(Modifier)
+                            } else {
+                                Row(
+                                    modifier = Modifier.fillMaxWidth(),
+                                    horizontalArrangement = spacedBy(16.dp),
+                                    verticalAlignment = Alignment.CenterVertically,
+                                ) {
+                                    content(Modifier.weight(1f))
+                                }
+                            }
                             Spacer(modifier = Modifier.height(16.dp))
                         }
                     },
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/SpatialAudioModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/SpatialAudioModule.kt
index da29d58..48af8cd 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/SpatialAudioModule.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/SpatialAudioModule.kt
@@ -16,16 +16,13 @@
 
 package com.android.systemui.volume.panel.component.spatialaudio
 
-import com.android.systemui.volume.panel.component.button.ui.composable.ButtonComponent
 import com.android.systemui.volume.panel.component.shared.model.VolumePanelComponents
 import com.android.systemui.volume.panel.component.spatial.domain.SpatialAudioAvailabilityCriteria
-import com.android.systemui.volume.panel.component.spatial.ui.viewmodel.SpatialAudioViewModel
-import com.android.systemui.volume.panel.component.spatialaudio.ui.composable.SpatialAudioPopup
+import com.android.systemui.volume.panel.component.spatialaudio.ui.composable.SpatialAudioComponent
 import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
 import com.android.systemui.volume.panel.shared.model.VolumePanelUiComponent
 import dagger.Binds
 import dagger.Module
-import dagger.Provides
 import dagger.multibindings.IntoMap
 import dagger.multibindings.StringKey
 
@@ -40,14 +37,8 @@
         criteria: SpatialAudioAvailabilityCriteria
     ): ComponentAvailabilityCriteria
 
-    companion object {
-
-        @Provides
-        @IntoMap
-        @StringKey(VolumePanelComponents.SPATIAL_AUDIO)
-        fun provideVolumePanelUiComponent(
-            viewModel: SpatialAudioViewModel,
-            popup: SpatialAudioPopup,
-        ): VolumePanelUiComponent = ButtonComponent(viewModel.spatialAudioButton, popup::show)
-    }
+    @Binds
+    @IntoMap
+    @StringKey(VolumePanelComponents.SPATIAL_AUDIO)
+    fun bindVolumePanelUiComponent(component: SpatialAudioComponent): VolumePanelUiComponent
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioComponent.kt
new file mode 100644
index 0000000..2d89b5c
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioComponent.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.spatialaudio.ui.composable
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.systemui.volume.panel.component.button.ui.composable.ButtonComponent
+import com.android.systemui.volume.panel.component.button.ui.composable.ToggleButtonComponent
+import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioEnabledModel
+import com.android.systemui.volume.panel.component.spatial.ui.viewmodel.SpatialAudioViewModel
+import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
+import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope
+import javax.inject.Inject
+
+/** [ComposeVolumePanelUiComponent] that represents spatial audio button in the Volume Panel. */
+class SpatialAudioComponent
+@Inject
+constructor(
+    private val viewModel: SpatialAudioViewModel,
+    private val popup: SpatialAudioPopup,
+) : ComposeVolumePanelUiComponent {
+
+    @Composable
+    override fun VolumePanelComposeScope.Content(modifier: Modifier) {
+        val shouldUsePopup by viewModel.shouldUsePopup.collectAsStateWithLifecycle()
+
+        val buttonComponent: ComposeVolumePanelUiComponent =
+            remember(shouldUsePopup) {
+                if (shouldUsePopup) {
+                    ButtonComponent(viewModel.spatialAudioButton, popup::show)
+                } else {
+                    ToggleButtonComponent(viewModel.spatialAudioButton) {
+                        if (it) {
+                            viewModel.setEnabled(SpatialAudioEnabledModel.SpatialAudioEnabled)
+                        } else {
+                            viewModel.setEnabled(SpatialAudioEnabledModel.Disabled)
+                        }
+                    }
+                }
+            }
+        with(buttonComponent) { Content(modifier) }
+    }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
index 271eb96..fbf91b7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
@@ -68,9 +68,7 @@
                     state.a11yClickDescription?.let {
                         customActions =
                             listOf(
-                                CustomAccessibilityAction(
-                                    it,
-                                ) {
+                                CustomAccessibilityAction(it) {
                                     onIconTapped()
                                     true
                                 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt
index ac5004e..580aba5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.volume.panel.ui.composable
 
+import androidx.compose.animation.AnimatedContent
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
@@ -56,17 +57,19 @@
                     with(component.component as ComposeVolumePanelUiComponent) { Content(Modifier) }
                 }
             }
-            Row(
-                modifier = Modifier.fillMaxWidth(),
-                horizontalArrangement = Arrangement.spacedBy(spacing),
-            ) {
-                for (component in layout.footerComponents) {
-                    AnimatedVisibility(
-                        visible = component.isVisible,
-                        modifier = Modifier.weight(1f),
-                    ) {
-                        with(component.component as ComposeVolumePanelUiComponent) {
-                            Content(Modifier)
+            AnimatedContent(
+                targetState = layout.footerComponents,
+                label = "FooterComponentAnimation",
+            ) { footerComponents ->
+                Row(
+                    modifier = Modifier.fillMaxWidth(),
+                    horizontalArrangement = Arrangement.spacedBy(spacing),
+                ) {
+                    for (component in footerComponents) {
+                        if (component.isVisible) {
+                            with(component.component as ComposeVolumePanelUiComponent) {
+                                Content(Modifier.weight(1f))
+                            }
                         }
                     }
                 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt
index 9ea20b9..6349c14 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.volume.panel.ui.composable
 
+import androidx.compose.animation.AnimatedContent
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
@@ -50,26 +51,27 @@
                 with(component.component as ComposeVolumePanelUiComponent) { Content(Modifier) }
             }
         }
-        if (layout.footerComponents.isNotEmpty()) {
+
+        AnimatedContent(
+            targetState = layout.footerComponents,
+            label = "FooterComponentAnimation",
+        ) { footerComponents ->
             Row(
                 modifier = Modifier.fillMaxWidth().wrapContentHeight(),
                 horizontalArrangement = Arrangement.spacedBy(if (isLargeScreen) 28.dp else 20.dp),
             ) {
                 val visibleComponentsCount =
-                    layout.footerComponents.fastSumBy { if (it.isVisible) 1 else 0 }
+                    footerComponents.fastSumBy { if (it.isVisible) 1 else 0 }
 
                 // Center footer component if there is only one present
                 if (visibleComponentsCount == 1) {
                     Spacer(modifier = Modifier.weight(0.5f))
                 }
 
-                for (component in layout.footerComponents) {
-                    AnimatedVisibility(
-                        visible = component.isVisible,
-                        modifier = Modifier.weight(1f),
-                    ) {
+                for (component in footerComponents) {
+                    if (component.isVisible) {
                         with(component.component as ComposeVolumePanelUiComponent) {
-                            Content(Modifier)
+                            Content(Modifier.weight(1f))
                         }
                     }
                 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
index 5d1a7c5..7fd3a176 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
@@ -27,11 +27,12 @@
 import androidx.compose.runtime.snapshotFlow
 import androidx.compose.runtime.snapshots.SnapshotStateMap
 import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.lerp
+import androidx.compose.ui.graphics.colorspace.ColorSpaces
 import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.lerp
+import androidx.compose.ui.unit.dp
 import androidx.compose.ui.util.fastCoerceIn
-import androidx.compose.ui.util.lerp
+import androidx.compose.ui.util.fastLastOrNull
+import kotlin.math.roundToInt
 
 /**
  * A [State] whose [value] is animated.
@@ -74,7 +75,7 @@
     key: ValueKey,
     canOverflow: Boolean = true,
 ): AnimatedState<Int> {
-    return animateSceneValueAsState(value, key, ::lerp, canOverflow)
+    return animateSceneValueAsState(value, key, SharedIntType, canOverflow)
 }
 
 /**
@@ -88,7 +89,19 @@
     key: ValueKey,
     canOverflow: Boolean = true,
 ): AnimatedState<Int> {
-    return animateElementValueAsState(value, key, ::lerp, canOverflow)
+    return animateElementValueAsState(value, key, SharedIntType, canOverflow)
+}
+
+private object SharedIntType : SharedValueType<Int, Int> {
+    override val unspecifiedValue: Int = Int.MIN_VALUE
+    override val zeroDeltaValue: Int = 0
+
+    override fun lerp(a: Int, b: Int, progress: Float): Int =
+        androidx.compose.ui.util.lerp(a, b, progress)
+
+    override fun diff(a: Int, b: Int): Int = a - b
+
+    override fun addWeighted(a: Int, b: Int, bWeight: Float): Int = (a + b * bWeight).roundToInt()
 }
 
 /**
@@ -102,7 +115,7 @@
     key: ValueKey,
     canOverflow: Boolean = true,
 ): AnimatedState<Float> {
-    return animateSceneValueAsState(value, key, ::lerp, canOverflow)
+    return animateSceneValueAsState(value, key, SharedFloatType, canOverflow)
 }
 
 /**
@@ -116,7 +129,19 @@
     key: ValueKey,
     canOverflow: Boolean = true,
 ): AnimatedState<Float> {
-    return animateElementValueAsState(value, key, ::lerp, canOverflow)
+    return animateElementValueAsState(value, key, SharedFloatType, canOverflow)
+}
+
+private object SharedFloatType : SharedValueType<Float, Float> {
+    override val unspecifiedValue: Float = Float.MIN_VALUE
+    override val zeroDeltaValue: Float = 0f
+
+    override fun lerp(a: Float, b: Float, progress: Float): Float =
+        androidx.compose.ui.util.lerp(a, b, progress)
+
+    override fun diff(a: Float, b: Float): Float = a - b
+
+    override fun addWeighted(a: Float, b: Float, bWeight: Float): Float = a + b * bWeight
 }
 
 /**
@@ -130,7 +155,7 @@
     key: ValueKey,
     canOverflow: Boolean = true,
 ): AnimatedState<Dp> {
-    return animateSceneValueAsState(value, key, ::lerp, canOverflow)
+    return animateSceneValueAsState(value, key, SharedDpType, canOverflow)
 }
 
 /**
@@ -144,7 +169,20 @@
     key: ValueKey,
     canOverflow: Boolean = true,
 ): AnimatedState<Dp> {
-    return animateElementValueAsState(value, key, ::lerp, canOverflow)
+    return animateElementValueAsState(value, key, SharedDpType, canOverflow)
+}
+
+private object SharedDpType : SharedValueType<Dp, Dp> {
+    override val unspecifiedValue: Dp = Dp.Unspecified
+    override val zeroDeltaValue: Dp = 0.dp
+
+    override fun lerp(a: Dp, b: Dp, progress: Float): Dp {
+        return androidx.compose.ui.unit.lerp(a, b, progress)
+    }
+
+    override fun diff(a: Dp, b: Dp): Dp = a - b
+
+    override fun addWeighted(a: Dp, b: Dp, bWeight: Float): Dp = a + b * bWeight
 }
 
 /**
@@ -157,7 +195,7 @@
     value: Color,
     key: ValueKey,
 ): AnimatedState<Color> {
-    return animateSceneValueAsState(value, key, ::lerp, canOverflow = false)
+    return animateSceneValueAsState(value, key, SharedColorType, canOverflow = false)
 }
 
 /**
@@ -170,9 +208,56 @@
     value: Color,
     key: ValueKey,
 ): AnimatedState<Color> {
-    return animateElementValueAsState(value, key, ::lerp, canOverflow = false)
+    return animateElementValueAsState(value, key, SharedColorType, canOverflow = false)
 }
 
+private object SharedColorType : SharedValueType<Color, ColorDelta> {
+    override val unspecifiedValue: Color = Color.Unspecified
+    override val zeroDeltaValue: ColorDelta = ColorDelta(0f, 0f, 0f, 0f)
+
+    override fun lerp(a: Color, b: Color, progress: Float): Color {
+        return androidx.compose.ui.graphics.lerp(a, b, progress)
+    }
+
+    override fun diff(a: Color, b: Color): ColorDelta {
+        // Similar to lerp, we convert colors to the Oklab color space to perform operations on
+        // colors.
+        val aOklab = a.convert(ColorSpaces.Oklab)
+        val bOklab = b.convert(ColorSpaces.Oklab)
+        return ColorDelta(
+            red = aOklab.red - bOklab.red,
+            green = aOklab.green - bOklab.green,
+            blue = aOklab.blue - bOklab.blue,
+            alpha = aOklab.alpha - bOklab.alpha,
+        )
+    }
+
+    override fun addWeighted(a: Color, b: ColorDelta, bWeight: Float): Color {
+        val aOklab = a.convert(ColorSpaces.Oklab)
+        return Color(
+                red = aOklab.red + b.red * bWeight,
+                green = aOklab.green + b.green * bWeight,
+                blue = aOklab.blue + b.blue * bWeight,
+                alpha = aOklab.alpha + b.alpha * bWeight,
+                colorSpace = ColorSpaces.Oklab,
+            )
+            .convert(aOklab.colorSpace)
+    }
+}
+
+/**
+ * Represents the diff between two colors in the same color space.
+ *
+ * Note: This class is necessary because Color() checks the bounds of its values and UncheckedColor
+ * is internal.
+ */
+private class ColorDelta(
+    val red: Float,
+    val green: Float,
+    val blue: Float,
+    val alpha: Float,
+)
+
 @Composable
 internal fun <T> animateSharedValueAsState(
     layoutImpl: SceneTransitionLayoutImpl,
@@ -180,23 +265,22 @@
     element: ElementKey?,
     key: ValueKey,
     value: T,
-    lerp: (T, T, Float) -> T,
+    type: SharedValueType<T, *>,
     canOverflow: Boolean,
 ): AnimatedState<T> {
     DisposableEffect(layoutImpl, scene, element, key) {
         // Create the associated maps that hold the current value for each (element, scene) pair.
         val valueMap = layoutImpl.sharedValues.getOrPut(key) { mutableMapOf() }
-        val sceneToValueMap =
-            valueMap.getOrPut(element) { SnapshotStateMap<SceneKey, Any>() }
-                as SnapshotStateMap<SceneKey, T>
-        sceneToValueMap[scene] = value
+        val sharedValue = valueMap.getOrPut(element) { SharedValue(type) } as SharedValue<T, *>
+        val targetValues = sharedValue.targetValues
+        targetValues[scene] = value
 
         onDispose {
             // Remove the value associated to the current scene, and eventually remove the maps if
             // they are empty.
-            sceneToValueMap.remove(scene)
+            targetValues.remove(scene)
 
-            if (sceneToValueMap.isEmpty() && valueMap[element] === sceneToValueMap) {
+            if (targetValues.isEmpty() && valueMap[element] === sharedValue) {
                 valueMap.remove(element)
 
                 if (valueMap.isEmpty() && layoutImpl.sharedValues[key] === valueMap) {
@@ -208,34 +292,25 @@
 
     // Update the current value. Note that side effects run after disposable effects, so we know
     // that the associated maps were created at this point.
-    SideEffect { sceneToValueMap<T>(layoutImpl, key, element)[scene] = value }
-
-    return remember(layoutImpl, scene, element, lerp, canOverflow) {
-        object : AnimatedState<T> {
-            override val value: T
-                get() = value(layoutImpl, scene, element, key, lerp, canOverflow)
-
-            @Composable
-            override fun unsafeCompositionState(initialValue: T): State<T> {
-                val state = remember { mutableStateOf(initialValue) }
-
-                val animatedState = this
-                LaunchedEffect(animatedState) {
-                    snapshotFlow { animatedState.value }.collect { state.value = it }
-                }
-
-                return state
-            }
+    SideEffect {
+        if (value == type.unspecifiedValue) {
+            error("value is equal to $value, which is the undefined value for this type.")
         }
+
+        sharedValue<T, Any>(layoutImpl, key, element).targetValues[scene] = value
+    }
+
+    return remember(layoutImpl, scene, element, canOverflow) {
+        AnimatedStateImpl<T, Any>(layoutImpl, scene, element, key, canOverflow)
     }
 }
 
-private fun <T> sceneToValueMap(
+private fun <T, Delta> sharedValue(
     layoutImpl: SceneTransitionLayoutImpl,
     key: ValueKey,
     element: ElementKey?
-): MutableMap<SceneKey, T> {
-    return layoutImpl.sharedValues[key]?.get(element)?.let { it as SnapshotStateMap<SceneKey, T> }
+): SharedValue<T, Delta> {
+    return layoutImpl.sharedValues[key]?.get(element)?.let { it as SharedValue<T, Delta> }
         ?: error(valueReadTooEarlyMessage(key))
 }
 
@@ -244,62 +319,155 @@
         "means that you are reading it during composition, which you should not do. See the " +
         "documentation of AnimatedState for more information."
 
-private fun <T> value(
-    layoutImpl: SceneTransitionLayoutImpl,
-    scene: SceneKey,
-    element: ElementKey?,
-    key: ValueKey,
-    lerp: (T, T, Float) -> T,
-    canOverflow: Boolean,
-): T {
-    return valueOrNull(layoutImpl, scene, element, key, lerp, canOverflow)
-        ?: error(valueReadTooEarlyMessage(key))
+internal class SharedValue<T, Delta>(
+    val type: SharedValueType<T, Delta>,
+) {
+    /** The target value of this shared value for each scene. */
+    val targetValues = SnapshotStateMap<SceneKey, T>()
+
+    /** The last value of this shared value. */
+    var lastValue: T = type.unspecifiedValue
+
+    /** The value of this shared value before the last interruption (if any). */
+    var valueBeforeInterruption: T = type.unspecifiedValue
+
+    /** The delta value to add to this shared value to have smoother interruptions. */
+    var valueInterruptionDelta = type.zeroDeltaValue
+
+    /** The last transition that was used when the value of this shared state. */
+    var lastTransition: TransitionState.Transition? = null
 }
 
-private fun <T> valueOrNull(
-    layoutImpl: SceneTransitionLayoutImpl,
-    scene: SceneKey,
-    element: ElementKey?,
-    key: ValueKey,
-    lerp: (T, T, Float) -> T,
-    canOverflow: Boolean,
-): T? {
-    val sceneToValueMap = sceneToValueMap<T>(layoutImpl, key, element)
-    fun sceneValue(scene: SceneKey): T? = sceneToValueMap[scene]
+private class AnimatedStateImpl<T, Delta>(
+    private val layoutImpl: SceneTransitionLayoutImpl,
+    private val scene: SceneKey,
+    private val element: ElementKey?,
+    private val key: ValueKey,
+    private val canOverflow: Boolean,
+) : AnimatedState<T> {
+    override val value: T
+        get() = value()
 
-    return when (val transition = layoutImpl.state.transitionState) {
-        is TransitionState.Idle -> sceneValue(transition.currentScene)
-        is TransitionState.Transition -> {
-            // Note: no need to check for transition ready here given that all target values are
-            // defined during composition, we should already have the correct values to interpolate
-            // between here.
-            val fromValue = sceneValue(transition.fromScene)
-            val toValue = sceneValue(transition.toScene)
-            if (fromValue != null && toValue != null) {
-                if (fromValue == toValue) {
-                    // Optimization: avoid reading progress if the values are the same, so we don't
-                    // relayout/redraw for nothing.
-                    fromValue
-                } else {
-                    // In the case of bouncing, if the value remains constant during the overscroll,
-                    // we should use the value of the scene we are bouncing around.
-                    if (!canOverflow && transition is TransitionState.HasOverscrollProperties) {
-                        val bouncingScene = transition.bouncingScene
-                        if (bouncingScene != null) {
-                            return sceneValue(bouncingScene)
-                        }
+    private fun value(): T {
+        val sharedValue = sharedValue<T, Delta>(layoutImpl, key, element)
+        val transition = transition(sharedValue)
+        val value: T =
+            valueOrNull(sharedValue, transition)
+                // TODO(b/311600838): Remove this. We should not have to fallback to the current
+                // scene value, but we have to because code of removed nodes can still run if they
+                // are placed with a graphics layer.
+                ?: sharedValue[scene]
+                ?: error(valueReadTooEarlyMessage(key))
+        val interruptedValue = computeInterruptedValue(sharedValue, transition, value)
+        sharedValue.lastValue = interruptedValue
+        return interruptedValue
+    }
+
+    private operator fun SharedValue<T, *>.get(scene: SceneKey): T? = targetValues[scene]
+
+    private fun valueOrNull(
+        sharedValue: SharedValue<T, *>,
+        transition: TransitionState.Transition?,
+    ): T? {
+        if (transition == null) {
+            return sharedValue[layoutImpl.state.transitionState.currentScene]
+        }
+
+        val fromValue = sharedValue[transition.fromScene]
+        val toValue = sharedValue[transition.toScene]
+        return if (fromValue != null && toValue != null) {
+            if (fromValue == toValue) {
+                // Optimization: avoid reading progress if the values are the same, so we don't
+                // relayout/redraw for nothing.
+                fromValue
+            } else {
+                // In the case of bouncing, if the value remains constant during the overscroll, we
+                // should use the value of the scene we are bouncing around.
+                if (!canOverflow && transition is TransitionState.HasOverscrollProperties) {
+                    val bouncingScene = transition.bouncingScene
+                    if (bouncingScene != null) {
+                        return sharedValue[bouncingScene]
                     }
-
-                    val progress =
-                        if (canOverflow) transition.progress
-                        else transition.progress.fastCoerceIn(0f, 1f)
-                    lerp(fromValue, toValue, progress)
                 }
-            } else fromValue ?: toValue
+
+                val progress =
+                    if (canOverflow) transition.progress
+                    else transition.progress.fastCoerceIn(0f, 1f)
+                sharedValue.type.lerp(fromValue, toValue, progress)
+            }
+        } else fromValue ?: toValue
+    }
+
+    private fun transition(sharedValue: SharedValue<T, Delta>): TransitionState.Transition? {
+        val targetValues = sharedValue.targetValues
+        val transition =
+            if (element != null) {
+                layoutImpl.elements[element]?.sceneStates?.let { sceneStates ->
+                    layoutImpl.state.currentTransitions.fastLastOrNull { transition ->
+                        transition.fromScene in sceneStates || transition.toScene in sceneStates
+                    }
+                }
+            } else {
+                layoutImpl.state.currentTransitions.fastLastOrNull { transition ->
+                    transition.fromScene in targetValues || transition.toScene in targetValues
+                }
+            }
+
+        val previousTransition = sharedValue.lastTransition
+        sharedValue.lastTransition = transition
+
+        if (transition != previousTransition && transition != null && previousTransition != null) {
+            // The previous transition was interrupted by another transition.
+            sharedValue.valueBeforeInterruption = sharedValue.lastValue
+            sharedValue.valueInterruptionDelta = sharedValue.type.zeroDeltaValue
+        } else if (transition == null && previousTransition != null) {
+            // The transition was just finished.
+            sharedValue.valueBeforeInterruption = sharedValue.type.unspecifiedValue
+            sharedValue.valueInterruptionDelta = sharedValue.type.zeroDeltaValue
+        }
+
+        return transition
+    }
+
+    /**
+     * Compute what [value] should be if we take the
+     * [interruption progress][TransitionState.Transition.interruptionProgress] of [transition] into
+     * account.
+     */
+    private fun computeInterruptedValue(
+        sharedValue: SharedValue<T, Delta>,
+        transition: TransitionState.Transition?,
+        value: T,
+    ): T {
+        val type = sharedValue.type
+        if (sharedValue.valueBeforeInterruption != type.unspecifiedValue) {
+            sharedValue.valueInterruptionDelta =
+                type.diff(sharedValue.valueBeforeInterruption, value)
+            sharedValue.valueBeforeInterruption = type.unspecifiedValue
+        }
+
+        val delta = sharedValue.valueInterruptionDelta
+        return if (delta == type.zeroDeltaValue || transition == null) {
+            value
+        } else {
+            val interruptionProgress = transition.interruptionProgress(layoutImpl)
+            if (interruptionProgress == 0f) {
+                value
+            } else {
+                type.addWeighted(value, delta, interruptionProgress)
+            }
         }
     }
-    // TODO(b/311600838): Remove this. We should not have to fallback to the current scene value,
-    // but we have to because code of removed nodes can still run if they are placed with a graphics
-    // layer.
-    ?: sceneValue(scene)
+
+    @Composable
+    override fun unsafeCompositionState(initialValue: T): State<T> {
+        val state = remember { mutableStateOf(initialValue) }
+
+        val animatedState = this
+        LaunchedEffect(animatedState) {
+            snapshotFlow { animatedState.value }.collect { state.value = it }
+        }
+
+        return state
+    }
 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/CommunalSwipeDetector.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/CommunalSwipeDetector.kt
new file mode 100644
index 0000000..7be34ca
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/CommunalSwipeDetector.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.animation.scene
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.ui.input.pointer.PointerInputChange
+import androidx.compose.ui.input.pointer.positionChange
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.IntOffset
+import androidx.compose.ui.unit.IntSize
+import kotlin.math.abs
+
+private const val TRAVEL_RATIO_THRESHOLD = .5f
+
+/**
+ * {@link CommunalSwipeDetector} provides an implementation of {@link SwipeDetector} and {@link
+ * SwipeSourceDetector} to enable fullscreen swipe handling to transition to and from the glanceable
+ * hub.
+ */
+class CommunalSwipeDetector(private var lastDirection: SwipeSource? = null) :
+    SwipeSourceDetector, SwipeDetector {
+    override fun source(
+        layoutSize: IntSize,
+        position: IntOffset,
+        density: Density,
+        orientation: Orientation
+    ): SwipeSource? {
+        return lastDirection
+    }
+
+    override fun detectSwipe(change: PointerInputChange): Boolean {
+        if (change.positionChange().x > 0) {
+            lastDirection = Edge.Left
+        } else {
+            lastDirection = Edge.Right
+        }
+
+        // Determine whether the ratio of the distance traveled horizontally to the distance
+        // traveled vertically exceeds the threshold.
+        return abs(change.positionChange().x / change.positionChange().y) > TRAVEL_RATIO_THRESHOLD
+    }
+}
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 1f81245..e9633c2 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
@@ -148,9 +148,9 @@
         val swipes = computeSwipes(fromScene, startedPosition, pointersDown)
         val result =
             swipes.findUserActionResult(fromScene, overSlop, true)
-            // As we were unable to locate a valid target scene, the initial SwipeTransition
-            // cannot be defined. Consequently, a simple NoOp Controller will be returned.
-            ?: return NoOpDragController
+                // As we were unable to locate a valid target scene, the initial SwipeTransition
+                // cannot be defined. Consequently, a simple NoOp Controller will be returned.
+                ?: return NoOpDragController
 
         return updateDragController(
             swipes = swipes,
@@ -521,6 +521,7 @@
         }
 
     return SwipeTransition(
+        layoutImpl = layoutImpl,
         layoutState = layoutState,
         coroutineScope = coroutineScope,
         key = result.transitionKey,
@@ -534,6 +535,7 @@
 
 private fun SwipeTransition(old: SwipeTransition): SwipeTransition {
     return SwipeTransition(
+            layoutImpl = old.layoutImpl,
             layoutState = old.layoutState,
             coroutineScope = old.coroutineScope,
             key = old.key,
@@ -550,6 +552,7 @@
 }
 
 private class SwipeTransition(
+    val layoutImpl: SceneTransitionLayoutImpl,
     val layoutState: BaseSceneTransitionLayoutState,
     val coroutineScope: CoroutineScope,
     val key: TransitionKey?,
@@ -607,6 +610,12 @@
 
     override val overscrollScope: OverscrollScope =
         object : OverscrollScope {
+            override val density: Float
+                get() = layoutImpl.density.density
+
+            override val fontScale: Float
+                get() = layoutImpl.density.fontScale
+
             override val absoluteDistance: Float
                 get() = distance().absoluteValue
         }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index d2a1de3..f0fb9f6 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -371,96 +371,57 @@
     transition: TransitionState.Transition,
     previousTransition: TransitionState.Transition,
 ) {
-    val previousUniqueState = reconcileStates(element, previousTransition)
-    if (previousUniqueState == null) {
-        reconcileStates(element, transition)
-        return
-    }
+    val sceneStates = element.sceneStates
+    sceneStates[previousTransition.fromScene]?.selfUpdateValuesBeforeInterruption()
+    sceneStates[previousTransition.toScene]?.selfUpdateValuesBeforeInterruption()
+    sceneStates[transition.fromScene]?.selfUpdateValuesBeforeInterruption()
+    sceneStates[transition.toScene]?.selfUpdateValuesBeforeInterruption()
 
-    val fromSceneState = element.sceneStates[transition.fromScene]
-    val toSceneState = element.sceneStates[transition.toScene]
-
-    if (
-        fromSceneState == null ||
-            toSceneState == null ||
-            sharedElementTransformation(element.key, transition)?.enabled != false
-    ) {
-        // If there is only one copy of the element or if the element is shared, animate deltas in
-        // both scenes.
-        fromSceneState?.updateValuesBeforeInterruption(previousUniqueState)
-        toSceneState?.updateValuesBeforeInterruption(previousUniqueState)
-    }
+    reconcileStates(element, previousTransition)
+    reconcileStates(element, transition)
 }
 
 /**
  * Reconcile the state of [element] in the fromScene and toScene of [transition] so that the values
  * before interruption have their expected values, taking shared transitions into account.
- *
- * If the element had a unique state, i.e. it is shared in [transition] or it is only present in one
- * of the scenes, return it.
  */
 private fun reconcileStates(
     element: Element,
     transition: TransitionState.Transition,
-): Element.SceneState? {
-    val fromSceneState = element.sceneStates[transition.fromScene]
-    val toSceneState = element.sceneStates[transition.toScene]
-    when {
-        // Element is in both scenes.
-        fromSceneState != null && toSceneState != null -> {
-            val isSharedTransformationDisabled =
-                sharedElementTransformation(element.key, transition)?.enabled == false
-            when {
-                // Element shared transition is disabled so the element is placed in both scenes.
-                isSharedTransformationDisabled -> {
-                    fromSceneState.updateValuesBeforeInterruption(fromSceneState)
-                    toSceneState.updateValuesBeforeInterruption(toSceneState)
-                    return null
-                }
+) {
+    val fromSceneState = element.sceneStates[transition.fromScene] ?: return
+    val toSceneState = element.sceneStates[transition.toScene] ?: return
+    if (!isSharedElementEnabled(element.key, transition)) {
+        return
+    }
 
-                // Element is shared and placed in fromScene only.
-                fromSceneState.lastOffset != Offset.Unspecified -> {
-                    fromSceneState.updateValuesBeforeInterruption(fromSceneState)
-                    toSceneState.updateValuesBeforeInterruption(fromSceneState)
-                    return fromSceneState
-                }
-
-                // Element is shared and placed in toScene only.
-                toSceneState.lastOffset != Offset.Unspecified -> {
-                    fromSceneState.updateValuesBeforeInterruption(toSceneState)
-                    toSceneState.updateValuesBeforeInterruption(toSceneState)
-                    return toSceneState
-                }
-
-                // Element is in none of the scenes.
-                else -> {
-                    fromSceneState.updateValuesBeforeInterruption(null)
-                    toSceneState.updateValuesBeforeInterruption(null)
-                    return null
-                }
-            }
-        }
-
-        // Element is only in fromScene.
-        fromSceneState != null -> {
-            fromSceneState.updateValuesBeforeInterruption(fromSceneState)
-            return fromSceneState
-        }
-
-        // Element is only in toScene.
-        toSceneState != null -> {
-            toSceneState.updateValuesBeforeInterruption(toSceneState)
-            return toSceneState
-        }
-        else -> return null
+    if (
+        fromSceneState.offsetBeforeInterruption != Offset.Unspecified &&
+            toSceneState.offsetBeforeInterruption == Offset.Unspecified
+    ) {
+        // Element is shared and placed in fromScene only.
+        toSceneState.updateValuesBeforeInterruption(fromSceneState)
+    } else if (
+        toSceneState.offsetBeforeInterruption != Offset.Unspecified &&
+            fromSceneState.offsetBeforeInterruption == Offset.Unspecified
+    ) {
+        // Element is shared and placed in toScene only.
+        fromSceneState.updateValuesBeforeInterruption(toSceneState)
     }
 }
 
-private fun Element.SceneState.updateValuesBeforeInterruption(lastState: Element.SceneState?) {
-    offsetBeforeInterruption = lastState?.lastOffset ?: Offset.Unspecified
-    sizeBeforeInterruption = lastState?.lastSize ?: Element.SizeUnspecified
-    scaleBeforeInterruption = lastState?.lastScale ?: Scale.Unspecified
-    alphaBeforeInterruption = lastState?.lastAlpha ?: Element.AlphaUnspecified
+private fun Element.SceneState.selfUpdateValuesBeforeInterruption() {
+    offsetBeforeInterruption = lastOffset
+    sizeBeforeInterruption = lastSize
+    scaleBeforeInterruption = lastScale
+    alphaBeforeInterruption = lastAlpha
+}
+
+private fun Element.SceneState.updateValuesBeforeInterruption(lastState: Element.SceneState) {
+    offsetBeforeInterruption = lastState.offsetBeforeInterruption
+    sizeBeforeInterruption = lastState.sizeBeforeInterruption
+    scaleBeforeInterruption = lastState.scaleBeforeInterruption
+    alphaBeforeInterruption = lastState.alphaBeforeInterruption
 
     clearInterruptionDeltas()
 }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
index 4b20aca..be005ea 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
@@ -77,7 +77,7 @@
     override fun <T> animateElementValueAsState(
         value: T,
         key: ValueKey,
-        lerp: (start: T, stop: T, fraction: Float) -> T,
+        type: SharedValueType<T, *>,
         canOverflow: Boolean
     ): AnimatedState<T> {
         return animateSharedValueAsState(
@@ -86,7 +86,7 @@
             element,
             key,
             value,
-            lerp,
+            type,
             canOverflow,
         )
     }
@@ -184,8 +184,7 @@
                 fromSceneZIndex = layoutImpl.scenes.getValue(transition.fromScene).zIndex,
                 toSceneZIndex = layoutImpl.scenes.getValue(transition.toScene).zIndex,
             ) != null
-        }
-            ?: return false
+        } ?: return false
 
     // Always compose movable elements in the scene picked by their scene picker.
     return shouldDrawOrComposeSharedElement(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
index 0fc0053..3cc8431 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MultiPointerDraggable.kt
@@ -72,6 +72,7 @@
     enabled: () -> Boolean,
     startDragImmediately: (startedPosition: Offset) -> Boolean,
     onDragStarted: (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
+    swipeDetector: SwipeDetector = DefaultSwipeDetector,
 ): Modifier =
     this.then(
         MultiPointerDraggableElement(
@@ -79,6 +80,7 @@
             enabled,
             startDragImmediately,
             onDragStarted,
+            swipeDetector,
         )
     )
 
@@ -88,6 +90,7 @@
     private val startDragImmediately: (startedPosition: Offset) -> Boolean,
     private val onDragStarted:
         (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
+    private val swipeDetector: SwipeDetector,
 ) : ModifierNodeElement<MultiPointerDraggableNode>() {
     override fun create(): MultiPointerDraggableNode =
         MultiPointerDraggableNode(
@@ -95,6 +98,7 @@
             enabled = enabled,
             startDragImmediately = startDragImmediately,
             onDragStarted = onDragStarted,
+            swipeDetector = swipeDetector,
         )
 
     override fun update(node: MultiPointerDraggableNode) {
@@ -102,6 +106,7 @@
         node.enabled = enabled
         node.startDragImmediately = startDragImmediately
         node.onDragStarted = onDragStarted
+        node.swipeDetector = swipeDetector
     }
 }
 
@@ -111,6 +116,7 @@
     var startDragImmediately: (startedPosition: Offset) -> Boolean,
     var onDragStarted:
         (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
+    var swipeDetector: SwipeDetector = DefaultSwipeDetector,
 ) :
     PointerInputModifierNode,
     DelegatingNode(),
@@ -199,6 +205,7 @@
                             onDragCancel = { controller ->
                                 controller.onStop(velocity = 0f, canChangeScene = true)
                             },
+                            swipeDetector = swipeDetector
                         )
                     } catch (exception: CancellationException) {
                         // If the coroutine scope is active, we can just restart the drag cycle.
@@ -226,7 +233,8 @@
             (startedPosition: Offset, overSlop: Float, pointersDown: Int) -> DragController,
         onDrag: (controller: DragController, change: PointerInputChange, dragAmount: Float) -> Unit,
         onDragEnd: (controller: DragController) -> Unit,
-        onDragCancel: (controller: DragController) -> Unit
+        onDragCancel: (controller: DragController) -> Unit,
+        swipeDetector: SwipeDetector,
     ) {
         // Wait for a consumable event in [PointerEventPass.Main] pass
         val consumablePointer = awaitConsumableEvent().changes.first()
@@ -238,8 +246,10 @@
                 consumablePointer
             } else {
                 val onSlopReached = { change: PointerInputChange, over: Float ->
-                    change.consume()
-                    overSlop = over
+                    if (swipeDetector.detectSwipe(change)) {
+                        change.consume()
+                        overSlop = over
+                    }
                 }
 
                 // TODO(b/291055080): Replace by await[Orientation]PointerSlopOrCancellation once it
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
index 6fef33c..936f4ba 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
@@ -24,7 +24,6 @@
 import androidx.compose.runtime.mutableFloatStateOf
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
-import androidx.compose.ui.ExperimentalComposeUiApi
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.layout.approachLayout
 import androidx.compose.ui.platform.testTag
@@ -69,7 +68,6 @@
     }
 
     @Composable
-    @OptIn(ExperimentalComposeUiApi::class)
     fun Content(modifier: Modifier = Modifier) {
         Box(
             modifier
@@ -96,6 +94,7 @@
     private val layoutImpl: SceneTransitionLayoutImpl,
     private val scene: Scene,
 ) : SceneScope, ElementStateScope by layoutImpl.elementStateScope {
+    override val sceneKey: SceneKey = scene.key
     override val layoutState: SceneTransitionLayoutState = layoutImpl.state
 
     override fun Modifier.element(key: ElementKey): Modifier {
@@ -124,7 +123,7 @@
     override fun <T> animateSceneValueAsState(
         value: T,
         key: ValueKey,
-        lerp: (T, T, Float) -> T,
+        type: SharedValueType<T, *>,
         canOverflow: Boolean
     ): AnimatedState<T> {
         return animateSharedValueAsState(
@@ -133,7 +132,7 @@
             element = null,
             key = key,
             value = value,
-            lerp = lerp,
+            type = type,
             canOverflow = canOverflow,
         )
     }
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index 11e711a..2946b04 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -55,6 +55,7 @@
     state: SceneTransitionLayoutState,
     modifier: Modifier = Modifier,
     swipeSourceDetector: SwipeSourceDetector = DefaultEdgeDetector,
+    swipeDetector: SwipeDetector = DefaultSwipeDetector,
     @FloatRange(from = 0.0, to = 0.5) transitionInterceptionThreshold: Float = 0f,
     scenes: SceneTransitionLayoutScope.() -> Unit,
 ) {
@@ -62,6 +63,7 @@
         state,
         modifier,
         swipeSourceDetector,
+        swipeDetector,
         transitionInterceptionThreshold,
         onLayoutImpl = null,
         scenes,
@@ -95,6 +97,7 @@
     transitions: SceneTransitions,
     modifier: Modifier = Modifier,
     swipeSourceDetector: SwipeSourceDetector = DefaultEdgeDetector,
+    swipeDetector: SwipeDetector = DefaultSwipeDetector,
     @FloatRange(from = 0.0, to = 0.5) transitionInterceptionThreshold: Float = 0f,
     enableInterruptions: Boolean = DEFAULT_INTERRUPTIONS_ENABLED,
     scenes: SceneTransitionLayoutScope.() -> Unit,
@@ -111,6 +114,7 @@
         state,
         modifier,
         swipeSourceDetector,
+        swipeDetector,
         transitionInterceptionThreshold,
         scenes,
     )
@@ -163,6 +167,9 @@
 @Stable
 @ElementDsl
 interface BaseSceneScope : ElementStateScope {
+    /** The key of this scene. */
+    val sceneKey: SceneKey
+
     /** The state of the [SceneTransitionLayout] in which this scene is contained. */
     val layoutState: SceneTransitionLayoutState
 
@@ -281,9 +288,7 @@
      *
      * @param value the value of this shared value in the current scene.
      * @param key the key of this shared value.
-     * @param lerp the *linear* interpolation function that should be used to interpolate between
-     *   two different values. Note that it has to be linear because the [fraction] passed to this
-     *   interpolator is already interpolated.
+     * @param type the [SharedValueType] of this animated value.
      * @param canOverflow whether this value can overflow past the values it is interpolated
      *   between, for instance because the transition is animated using a bouncy spring.
      * @see animateSceneIntAsState
@@ -295,11 +300,39 @@
     fun <T> animateSceneValueAsState(
         value: T,
         key: ValueKey,
-        lerp: (start: T, stop: T, fraction: Float) -> T,
+        type: SharedValueType<T, *>,
         canOverflow: Boolean,
     ): AnimatedState<T>
 }
 
+/**
+ * The type of a shared value animated using [ElementScope.animateElementValueAsState] or
+ * [SceneScope.animateSceneValueAsState].
+ */
+@Stable
+interface SharedValueType<T, Delta> {
+    /** The unspecified value for this type. */
+    val unspecifiedValue: T
+
+    /**
+     * The zero value of this type. It should be equal to what [diff(x, x)] returns for any value of
+     * x.
+     */
+    val zeroDeltaValue: Delta
+
+    /**
+     * Return the linear interpolation of [a] and [b] at the given [progress], i.e. `a + (b - a) *
+     * progress`.
+     */
+    fun lerp(a: T, b: T, progress: Float): T
+
+    /** Return `a - b`. */
+    fun diff(a: T, b: T): Delta
+
+    /** Return `a + b * bWeight`. */
+    fun addWeighted(a: T, b: Delta, bWeight: Float): T
+}
+
 @Stable
 @ElementDsl
 interface ElementScope<ContentScope> {
@@ -308,9 +341,7 @@
      *
      * @param value the value of this shared value in the current scene.
      * @param key the key of this shared value.
-     * @param lerp the *linear* interpolation function that should be used to interpolate between
-     *   two different values. Note that it has to be linear because the [fraction] passed to this
-     *   interpolator is already interpolated.
+     * @param type the [SharedValueType] of this animated value.
      * @param canOverflow whether this value can overflow past the values it is interpolated
      *   between, for instance because the transition is animated using a bouncy spring.
      * @see animateElementIntAsState
@@ -322,7 +353,7 @@
     fun <T> animateElementValueAsState(
         value: T,
         key: ValueKey,
-        lerp: (start: T, stop: T, fraction: Float) -> T,
+        type: SharedValueType<T, *>,
         canOverflow: Boolean,
     ): AnimatedState<T>
 
@@ -467,6 +498,7 @@
     state: SceneTransitionLayoutState,
     modifier: Modifier = Modifier,
     swipeSourceDetector: SwipeSourceDetector = DefaultEdgeDetector,
+    swipeDetector: SwipeDetector = DefaultSwipeDetector,
     transitionInterceptionThreshold: Float = 0f,
     onLayoutImpl: ((SceneTransitionLayoutImpl) -> Unit)? = null,
     scenes: SceneTransitionLayoutScope.() -> Unit,
@@ -502,5 +534,5 @@
         layoutImpl.transitionInterceptionThreshold = transitionInterceptionThreshold
     }
 
-    layoutImpl.Content(modifier)
+    layoutImpl.Content(modifier, swipeDetector)
 }
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 7856498..5fa7c87 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
@@ -85,15 +85,14 @@
      * The different values of a shared value keyed by a a [ValueKey] and the different elements and
      * scenes it is associated to.
      */
-    private var _sharedValues:
-        MutableMap<ValueKey, MutableMap<ElementKey?, SnapshotStateMap<SceneKey, *>>>? =
+    private var _sharedValues: MutableMap<ValueKey, MutableMap<ElementKey?, SharedValue<*, *>>>? =
         null
-    internal val sharedValues:
-        MutableMap<ValueKey, MutableMap<ElementKey?, SnapshotStateMap<SceneKey, *>>>
+    internal val sharedValues: MutableMap<ValueKey, MutableMap<ElementKey?, SharedValue<*, *>>>
         get() =
             _sharedValues
-                ?: mutableMapOf<ValueKey, MutableMap<ElementKey?, SnapshotStateMap<SceneKey, *>>>()
-                    .also { _sharedValues = it }
+                ?: mutableMapOf<ValueKey, MutableMap<ElementKey?, SharedValue<*, *>>>().also {
+                    _sharedValues = it
+                }
 
     // TODO(b/317958526): Lazily allocate scene gesture handlers the first time they are needed.
     private val horizontalDraggableHandler: DraggableHandlerImpl
@@ -185,14 +184,14 @@
     }
 
     @Composable
-    internal fun Content(modifier: Modifier) {
+    internal fun Content(modifier: Modifier, swipeDetector: SwipeDetector) {
         Box(
             modifier
                 // Handle horizontal and vertical swipes on this layout.
                 // Note: order here is important and will give a slight priority to the vertical
                 // swipes.
-                .swipeToScene(horizontalDraggableHandler)
-                .swipeToScene(verticalDraggableHandler)
+                .swipeToScene(horizontalDraggableHandler, swipeDetector)
+                .swipeToScene(verticalDraggableHandler, swipeDetector)
                 .then(LayoutElement(layoutImpl = this))
         ) {
             LookaheadScope {
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 a5b6d24..44affd9 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
@@ -457,7 +457,7 @@
      */
     internal fun startTransition(
         transition: TransitionState.Transition,
-        transitionKey: TransitionKey?,
+        transitionKey: TransitionKey? = null,
         chain: Boolean = true,
     ) {
         checkThread()
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeDetector.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeDetector.kt
new file mode 100644
index 0000000..54ee783
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeDetector.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation.scene
+
+import androidx.compose.runtime.Stable
+import androidx.compose.ui.input.pointer.PointerInputChange
+
+/** {@link SwipeDetector} helps determine whether a swipe gestured has occurred. */
+@Stable
+interface SwipeDetector {
+    /**
+     * Invoked on changes to pointer input. Returns {@code true} if a swipe has been recognized,
+     * {@code false} otherwise.
+     */
+    fun detectSwipe(change: PointerInputChange): Boolean
+}
+
+val DefaultSwipeDetector = PassthroughSwipeDetector()
+
+/** An {@link SwipeDetector} implementation that recognizes a swipe on any input. */
+class PassthroughSwipeDetector : SwipeDetector {
+    override fun detectSwipe(change: PointerInputChange): Boolean {
+        // Simply accept all changes as a swipe
+        return true
+    }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index b618369..171e243 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -31,14 +31,18 @@
  * Configures the swipeable behavior of a [SceneTransitionLayout] depending on the current state.
  */
 @Stable
-internal fun Modifier.swipeToScene(draggableHandler: DraggableHandlerImpl): Modifier {
-    return this.then(SwipeToSceneElement(draggableHandler))
+internal fun Modifier.swipeToScene(
+    draggableHandler: DraggableHandlerImpl,
+    swipeDetector: SwipeDetector
+): Modifier {
+    return this.then(SwipeToSceneElement(draggableHandler, swipeDetector))
 }
 
 private data class SwipeToSceneElement(
     val draggableHandler: DraggableHandlerImpl,
+    val swipeDetector: SwipeDetector
 ) : ModifierNodeElement<SwipeToSceneNode>() {
-    override fun create(): SwipeToSceneNode = SwipeToSceneNode(draggableHandler)
+    override fun create(): SwipeToSceneNode = SwipeToSceneNode(draggableHandler, swipeDetector)
 
     override fun update(node: SwipeToSceneNode) {
         node.draggableHandler = draggableHandler
@@ -47,6 +51,7 @@
 
 private class SwipeToSceneNode(
     draggableHandler: DraggableHandlerImpl,
+    swipeDetector: SwipeDetector,
 ) : DelegatingNode(), PointerInputModifierNode {
     private val delegate =
         delegate(
@@ -55,6 +60,7 @@
                 enabled = ::enabled,
                 startDragImmediately = ::startDragImmediately,
                 onDragStarted = draggableHandler::onDragStarted,
+                swipeDetector = swipeDetector,
             )
         )
 
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
index a4682ff..465a410 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
@@ -20,6 +20,7 @@
 import androidx.compose.animation.core.SpringSpec
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import com.android.compose.animation.scene.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
@@ -192,7 +193,7 @@
     )
 }
 
-interface OverscrollScope {
+interface OverscrollScope : Density {
     /**
      * Return the absolute distance between fromScene and toScene, if available, otherwise
      * [DistanceUnspecified].
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
index e8854cf..6e8b208 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
@@ -32,7 +32,12 @@
 import androidx.compose.ui.unit.lerp
 import androidx.compose.ui.util.lerp
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.TestScenes.SceneA
+import com.android.compose.animation.scene.TestScenes.SceneB
+import com.android.compose.animation.scene.TestScenes.SceneC
+import com.android.compose.animation.scene.TestScenes.SceneD
 import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertThrows
 import org.junit.Rule
 import org.junit.Test
@@ -130,8 +135,8 @@
                 // The transition lasts 64ms = 4 frames.
                 spec = tween(durationMillis = 16 * 4, easing = LinearEasing)
             },
-            fromScene = TestScenes.SceneA,
-            toScene = TestScenes.SceneB,
+            fromScene = SceneA,
+            toScene = SceneB,
         ) {
             before {
                 assertThat(lastValueInFrom).isEqualTo(fromValues)
@@ -189,8 +194,8 @@
                 // The transition lasts 64ms = 4 frames.
                 spec = tween(durationMillis = 16 * 4, easing = LinearEasing)
             },
-            fromScene = TestScenes.SceneA,
-            toScene = TestScenes.SceneB,
+            fromScene = SceneA,
+            toScene = SceneB,
         ) {
             before {
                 assertThat(lastValueInFrom).isEqualTo(fromValues)
@@ -243,8 +248,8 @@
                 // The transition lasts 64ms = 4 frames.
                 spec = tween(durationMillis = 16 * 4, easing = LinearEasing)
             },
-            fromScene = TestScenes.SceneA,
-            toScene = TestScenes.SceneB,
+            fromScene = SceneA,
+            toScene = SceneB,
         ) {
             before {
                 assertThat(lastValueInFrom).isEqualTo(fromValues)
@@ -381,4 +386,61 @@
             }
         }
     }
+
+    @Test
+    fun animatedValueIsUsingLastTransition() = runTest {
+        val state =
+            rule.runOnUiThread { MutableSceneTransitionLayoutStateImpl(SceneA, transitions {}) }
+
+        val foo = ValueKey("foo")
+        val bar = ValueKey("bar")
+        val lastValues = mutableMapOf<ValueKey, MutableMap<SceneKey, Float>>()
+
+        @Composable
+        fun SceneScope.animateFloat(value: Float, key: ValueKey) {
+            val animatedValue = animateSceneFloatAsState(value, key)
+            LaunchedEffect(animatedValue) {
+                snapshotFlow { animatedValue.value }
+                    .collect { lastValues.getOrPut(key) { mutableMapOf() }[sceneKey] = it }
+            }
+        }
+
+        rule.setContent {
+            SceneTransitionLayout(state) {
+                // foo goes from 0f to 100f in A => B.
+                scene(SceneA) { animateFloat(0f, foo) }
+                scene(SceneB) { animateFloat(100f, foo) }
+
+                // bar goes from 0f to 10f in C => D.
+                scene(SceneC) { animateFloat(0f, bar) }
+                scene(SceneD) { animateFloat(10f, bar) }
+            }
+        }
+
+        rule.runOnUiThread {
+            // A => B is at 30%.
+            state.startTransition(
+                transition(
+                    from = SceneA,
+                    to = SceneB,
+                    progress = { 0.3f },
+                    onFinish = neverFinish(),
+                )
+            )
+
+            // C => D is at 70%.
+            state.startTransition(transition(from = SceneC, to = SceneD, progress = { 0.7f }))
+        }
+        rule.waitForIdle()
+
+        assertThat(lastValues[foo]?.get(SceneA)).isWithin(0.001f).of(30f)
+        assertThat(lastValues[foo]?.get(SceneB)).isWithin(0.001f).of(30f)
+        assertThat(lastValues[foo]?.get(SceneC)).isNull()
+        assertThat(lastValues[foo]?.get(SceneD)).isNull()
+
+        assertThat(lastValues[bar]?.get(SceneA)).isNull()
+        assertThat(lastValues[bar]?.get(SceneB)).isNull()
+        assertThat(lastValues[bar]?.get(SceneC)).isWithin(0.001f).of(7f)
+        assertThat(lastValues[bar]?.get(SceneD)).isWithin(0.001f).of(7f)
+    }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index 9692fae..beb74bc 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -59,6 +59,7 @@
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.lerp
+import androidx.compose.ui.util.lerp
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.compose.animation.scene.TestScenes.SceneA
 import com.android.compose.animation.scene.TestScenes.SceneB
@@ -348,7 +349,7 @@
                     ),
                 onLayoutImpl = { nullableLayoutImpl = it },
             ) {
-                scene(SceneA) { /* Nothing */}
+                scene(SceneA) { /* Nothing */ }
                 scene(SceneB) { Box(Modifier.element(key)) }
                 scene(SceneC) {
                     when (sceneCState) {
@@ -1083,10 +1084,17 @@
             }
 
         val layoutSize = DpSize(200.dp, 100.dp)
+        val lastValues = mutableMapOf<SceneKey, Float>()
 
         @Composable
-        fun SceneScope.Foo(size: Dp, modifier: Modifier = Modifier) {
-            Box(modifier.element(TestElements.Foo).size(size))
+        fun SceneScope.Foo(size: Dp, value: Float, modifier: Modifier = Modifier) {
+            val sceneKey = this.sceneKey
+            Element(TestElements.Foo, modifier.size(size)) {
+                val animatedValue = animateElementFloatAsState(value, TestValues.Value1)
+                LaunchedEffect(animatedValue) {
+                    snapshotFlow { animatedValue.value }.collect { lastValues[sceneKey] = it }
+                }
+            }
         }
 
         // The size of Foo when idle in A, B or C.
@@ -1094,6 +1102,11 @@
         val sizeInB = 30.dp
         val sizeInC = 50.dp
 
+        // The target value when idle in A, B, or C.
+        val valueInA = 0f
+        val valueInB = 100f
+        val valueInC = 200f
+
         lateinit var layoutImpl: SceneTransitionLayoutImpl
         rule.setContent {
             SceneTransitionLayoutForTesting(
@@ -1103,7 +1116,9 @@
             ) {
                 // In scene A, Foo is aligned at the TopStart.
                 scene(SceneA) {
-                    Box(Modifier.fillMaxSize()) { Foo(sizeInA, Modifier.align(Alignment.TopStart)) }
+                    Box(Modifier.fillMaxSize()) {
+                        Foo(sizeInA, valueInA, Modifier.align(Alignment.TopStart))
+                    }
                 }
 
                 // In scene C, Foo is aligned at the BottomEnd, so it moves vertically when coming
@@ -1111,14 +1126,16 @@
                 // values and deltas are properly cleared once all transitions are done.
                 scene(SceneC) {
                     Box(Modifier.fillMaxSize()) {
-                        Foo(sizeInC, Modifier.align(Alignment.BottomEnd))
+                        Foo(sizeInC, valueInC, Modifier.align(Alignment.BottomEnd))
                     }
                 }
 
                 // In scene B, Foo is aligned at the TopEnd, so it moves horizontally when coming
                 // from A.
                 scene(SceneB) {
-                    Box(Modifier.fillMaxSize()) { Foo(sizeInB, Modifier.align(Alignment.TopEnd)) }
+                    Box(Modifier.fillMaxSize()) {
+                        Foo(sizeInB, valueInB, Modifier.align(Alignment.TopEnd))
+                    }
                 }
             }
         }
@@ -1134,6 +1151,10 @@
             .assertSizeIsEqualTo(sizeInA)
             .assertPositionInRootIsEqualTo(offsetInA.x, offsetInA.y)
 
+        assertThat(lastValues[SceneA]).isWithin(0.001f).of(valueInA)
+        assertThat(lastValues[SceneB]).isNull()
+        assertThat(lastValues[SceneC]).isNull()
+
         // Current transition is A => B at 50%.
         val aToBProgress = 0.5f
         val aToB =
@@ -1145,12 +1166,17 @@
             )
         val offsetInAToB = lerp(offsetInA, offsetInB, aToBProgress)
         val sizeInAToB = lerp(sizeInA, sizeInB, aToBProgress)
+        val valueInAToB = lerp(valueInA, valueInB, aToBProgress)
         rule.runOnUiThread { state.startTransition(aToB, transitionKey = null) }
         rule
             .onNode(isElement(TestElements.Foo, SceneB))
             .assertSizeIsEqualTo(sizeInAToB)
             .assertPositionInRootIsEqualTo(offsetInAToB.x, offsetInAToB.y)
 
+        assertThat(lastValues[SceneA]).isWithin(0.001f).of(valueInAToB)
+        assertThat(lastValues[SceneB]).isWithin(0.001f).of(valueInAToB)
+        assertThat(lastValues[SceneC]).isNull()
+
         // Start B => C at 0%.
         var bToCProgress by mutableFloatStateOf(0f)
         var interruptionProgress by mutableFloatStateOf(1f)
@@ -1167,6 +1193,11 @@
         // to the current transition offset and size.
         val offsetInterruptionDelta = offsetInAToB - offsetInB
         val sizeInterruptionDelta = sizeInAToB - sizeInB
+        val valueInterruptionDelta = valueInAToB - valueInB
+
+        assertThat(offsetInterruptionDelta).isNotEqualTo(DpOffset.Zero)
+        assertThat(sizeInterruptionDelta).isNotEqualTo(0.dp)
+        assertThat(valueInterruptionDelta).isNotEqualTo(0f)
 
         // Interruption progress is at 100% and bToC is at 0%, so Foo should be at the same offset
         // and size as right before the interruption.
@@ -1175,11 +1206,16 @@
             .assertPositionInRootIsEqualTo(offsetInAToB.x, offsetInAToB.y)
             .assertSizeIsEqualTo(sizeInAToB)
 
+        assertThat(lastValues[SceneA]).isWithin(0.001f).of(valueInAToB)
+        assertThat(lastValues[SceneB]).isWithin(0.001f).of(valueInAToB)
+        assertThat(lastValues[SceneC]).isWithin(0.001f).of(valueInAToB)
+
         // Move the transition forward at 30% and set the interruption progress to 50%.
         bToCProgress = 0.3f
         interruptionProgress = 0.5f
         val offsetInBToC = lerp(offsetInB, offsetInC, bToCProgress)
         val sizeInBToC = lerp(sizeInB, sizeInC, bToCProgress)
+        val valueInBToC = lerp(valueInB, valueInC, bToCProgress)
         val offsetInBToCWithInterruption =
             offsetInBToC +
                 DpOffset(
@@ -1187,6 +1223,9 @@
                     offsetInterruptionDelta.y * interruptionProgress,
                 )
         val sizeInBToCWithInterruption = sizeInBToC + sizeInterruptionDelta * interruptionProgress
+        val valueInBToCWithInterruption =
+            valueInBToC + valueInterruptionDelta * interruptionProgress
+
         rule.waitForIdle()
         rule
             .onNode(isElement(TestElements.Foo, SceneB))
@@ -1196,6 +1235,10 @@
             )
             .assertSizeIsEqualTo(sizeInBToCWithInterruption)
 
+        assertThat(lastValues[SceneA]).isWithin(0.001f).of(valueInBToCWithInterruption)
+        assertThat(lastValues[SceneB]).isWithin(0.001f).of(valueInBToCWithInterruption)
+        assertThat(lastValues[SceneC]).isWithin(0.001f).of(valueInBToCWithInterruption)
+
         // Finish the transition and interruption.
         bToCProgress = 1f
         interruptionProgress = 0f
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
index aa6d113..4bb643f 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/MultiPointerDraggableTest.kt
@@ -30,6 +30,7 @@
 import androidx.compose.ui.geometry.Size
 import androidx.compose.ui.input.pointer.AwaitPointerEventScope
 import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.input.pointer.PointerInputChange
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.platform.LocalDensity
 import androidx.compose.ui.platform.LocalViewConfiguration
@@ -346,4 +347,69 @@
         continueDraggingDown()
         assertThat(stopped).isTrue()
     }
+
+    @Test
+    fun multiPointerSwipeDetectorInteraction() {
+        val size = 200f
+        val middle = Offset(size / 2f, size / 2f)
+
+        var started = false
+
+        var capturedChange: PointerInputChange? = null
+        var swipeConsume = false
+
+        var touchSlop = 0f
+        rule.setContent {
+            touchSlop = LocalViewConfiguration.current.touchSlop
+            Box(
+                Modifier.size(with(LocalDensity.current) { Size(size, size).toDpSize() })
+                    .multiPointerDraggable(
+                        orientation = Orientation.Vertical,
+                        enabled = { true },
+                        startDragImmediately = { false },
+                        swipeDetector =
+                            object : SwipeDetector {
+                                override fun detectSwipe(change: PointerInputChange): Boolean {
+                                    capturedChange = change
+                                    return swipeConsume
+                                }
+                            },
+                        onDragStarted = { _, _, _ ->
+                            started = true
+                            object : DragController {
+                                override fun onDrag(delta: Float) {}
+
+                                override fun onStop(velocity: Float, canChangeScene: Boolean) {}
+                            }
+                        },
+                    )
+            ) {}
+        }
+
+        fun startDraggingDown() {
+            rule.onRoot().performTouchInput {
+                down(middle)
+                moveBy(Offset(0f, touchSlop))
+            }
+        }
+
+        fun continueDraggingDown() {
+            rule.onRoot().performTouchInput { moveBy(Offset(0f, touchSlop)) }
+        }
+
+        startDraggingDown()
+        assertThat(capturedChange).isNotNull()
+        capturedChange = null
+        assertThat(started).isFalse()
+
+        swipeConsume = true
+        continueDraggingDown()
+        assertThat(capturedChange).isNotNull()
+        capturedChange = null
+
+        continueDraggingDown()
+        assertThat(capturedChange).isNull()
+
+        assertThat(started).isTrue()
+    }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
index 3751a22..08532bd 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
@@ -461,4 +461,30 @@
         assertThat(exception).hasMessageThat().contains(Back.toString())
         assertThat(exception).hasMessageThat().contains(SceneA.debugName)
     }
+
+    @Test
+    fun sceneKeyInScope() {
+        val state = rule.runOnUiThread { MutableSceneTransitionLayoutState(SceneA) }
+
+        var keyInA: SceneKey? = null
+        var keyInB: SceneKey? = null
+        var keyInC: SceneKey? = null
+        rule.setContent {
+            SceneTransitionLayout(state) {
+                scene(SceneA) { keyInA = sceneKey }
+                scene(SceneB) { keyInB = sceneKey }
+                scene(SceneC) { keyInC = sceneKey }
+            }
+        }
+
+        // Snap to B then C to compose these scenes at least once.
+        rule.runOnUiThread { state.snapToScene(SceneB) }
+        rule.waitForIdle()
+        rule.runOnUiThread { state.snapToScene(SceneC) }
+        rule.waitForIdle()
+
+        assertThat(keyInA).isEqualTo(SceneA)
+        assertThat(keyInB).isEqualTo(SceneB)
+        assertThat(keyInC).isEqualTo(SceneC)
+    }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index 3a806a4..25ea2ee 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -30,6 +30,7 @@
 import androidx.compose.ui.geometry.Offset
 import androidx.compose.ui.platform.LocalViewConfiguration
 import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertPositionInRootIsEqualTo
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onRoot
 import androidx.compose.ui.test.performTouchInput
@@ -594,4 +595,43 @@
         assertThat(transition).hasToScene(SceneB)
         assertThat(transition).hasProgress(0.5f, tolerance = 0.01f)
     }
+
+    @Test
+    fun overscrollScopeExtendsDensity() {
+        val swipeDistance = 100.dp
+        val state =
+            rule.runOnUiThread {
+                MutableSceneTransitionLayoutState(
+                    SceneA,
+                    transitions {
+                        from(SceneA, to = SceneB) { distance = FixedDistance(swipeDistance) }
+
+                        overscroll(SceneB, Orientation.Vertical) {
+                            translate(TestElements.Foo, x = { 20.dp.toPx() }, y = { 30.dp.toPx() })
+                        }
+                    }
+                )
+            }
+        val layoutSize = 200.dp
+        var touchSlop = 0f
+        rule.setContent {
+            touchSlop = LocalViewConfiguration.current.touchSlop
+            SceneTransitionLayout(state, Modifier.size(layoutSize)) {
+                scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
+                    Box(Modifier.fillMaxSize())
+                }
+                scene(SceneB) { Box(Modifier.element(TestElements.Foo).fillMaxSize()) }
+            }
+        }
+
+        // Swipe down by twice the swipe distance so that we are at 100% overscrolling on scene B.
+        rule.onRoot().performTouchInput {
+            val middle = (layoutSize / 2).toPx()
+            down(Offset(middle, middle))
+            moveBy(Offset(0f, touchSlop + (swipeDistance * 2).toPx()), delayMillis = 1_000)
+        }
+
+        // Foo should be translated by (20dp, 30dp).
+        rule.onNode(isElement(TestElements.Foo)).assertPositionInRootIsEqualTo(20.dp, 30.dp)
+    }
 }
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
index a609be4..e6fa69d 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
@@ -41,8 +41,10 @@
     return object : TransitionState.Transition(from, to), TransitionState.HasOverscrollProperties {
         override val currentScene: SceneKey
             get() = current()
+
         override val progress: Float
             get() = progress()
+
         override val progressVelocity: Float
             get() = progressVelocity()
 
@@ -53,6 +55,8 @@
         override val orientation: Orientation = orientation
         override val overscrollScope: OverscrollScope =
             object : OverscrollScope {
+                override val density: Float = 1f
+                override val fontScale: Float = 1f
                 override val absoluteDistance = 0f
             }
 
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 bdeab79..079c1d9 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
@@ -17,7 +17,6 @@
 
 import android.animation.TimeInterpolator
 import android.annotation.ColorInt
-import android.annotation.FloatRange
 import android.annotation.IntRange
 import android.annotation.SuppressLint
 import android.content.Context
@@ -27,7 +26,7 @@
 import android.text.format.DateFormat
 import android.util.AttributeSet
 import android.util.MathUtils.constrainedMap
-import android.util.TypedValue
+import android.util.TypedValue.COMPLEX_UNIT_PX
 import android.view.View
 import android.view.View.MeasureSpec.EXACTLY
 import android.widget.TextView
@@ -219,9 +218,7 @@
 
     override fun setTextSize(type: Int, size: Float) {
         super.setTextSize(type, size)
-        if (type == TypedValue.COMPLEX_UNIT_PX) {
-            lastUnconstrainedTextSize = size
-        }
+        lastUnconstrainedTextSize = if (type == COMPLEX_UNIT_PX) size else Float.MAX_VALUE
     }
 
     @SuppressLint("DrawAllocation")
@@ -234,23 +231,19 @@
                 MeasureSpec.getMode(heightMeasureSpec) == EXACTLY
         ) {
             // Call straight into TextView.setTextSize to avoid setting lastUnconstrainedTextSize
-            super.setTextSize(
-                TypedValue.COMPLEX_UNIT_PX,
-                min(lastUnconstrainedTextSize, MeasureSpec.getSize(heightMeasureSpec) / 2F)
-            )
+            val size = min(lastUnconstrainedTextSize, MeasureSpec.getSize(heightMeasureSpec) / 2F)
+            super.setTextSize(COMPLEX_UNIT_PX, size)
         }
 
         super.onMeasure(widthMeasureSpec, heightMeasureSpec)
-        val animator = textAnimator
-        if (animator == null) {
-            textAnimator =
-                textAnimatorFactory(layout, ::invalidate)?.also {
-                    onTextAnimatorInitialized?.invoke(it)
-                    onTextAnimatorInitialized = null
-                }
-        } else {
-            animator.updateLayout(layout)
-        }
+        textAnimator?.let { animator -> animator.updateLayout(layout, textSize) }
+            ?: run {
+                textAnimator =
+                    textAnimatorFactory(layout, ::invalidate).also {
+                        onTextAnimatorInitialized?.invoke(it)
+                        onTextAnimatorInitialized = null
+                    }
+            }
 
         if (migratedClocks && hasCustomPositionUpdatedAnimation) {
             // Expand width to avoid clock being clipped during stepping animation
@@ -307,18 +300,18 @@
         logger.d("animateColorChange")
         setTextStyle(
             weight = lockScreenWeight,
-            textSize = -1f,
             color = null, /* using current color */
             animate = false,
+            interpolator = null,
             duration = 0,
             delay = 0,
             onAnimationEnd = null
         )
         setTextStyle(
             weight = lockScreenWeight,
-            textSize = -1f,
             color = lockScreenColor,
             animate = true,
+            interpolator = null,
             duration = COLOR_ANIM_DURATION,
             delay = 0,
             onAnimationEnd = null
@@ -329,16 +322,15 @@
         logger.d("animateAppearOnLockscreen")
         setTextStyle(
             weight = dozingWeight,
-            textSize = -1f,
             color = lockScreenColor,
             animate = false,
+            interpolator = null,
             duration = 0,
             delay = 0,
             onAnimationEnd = null
         )
         setTextStyle(
             weight = lockScreenWeight,
-            textSize = -1f,
             color = lockScreenColor,
             animate = true,
             duration = APPEAR_ANIM_DURATION,
@@ -356,16 +348,15 @@
         logger.d("animateFoldAppear")
         setTextStyle(
             weight = lockScreenWeightInternal,
-            textSize = -1f,
             color = lockScreenColor,
             animate = false,
+            interpolator = null,
             duration = 0,
             delay = 0,
             onAnimationEnd = null
         )
         setTextStyle(
             weight = dozingWeightInternal,
-            textSize = -1f,
             color = dozingColor,
             animate = animate,
             interpolator = Interpolators.EMPHASIZED_DECELERATE,
@@ -385,9 +376,9 @@
         val startAnimPhase2 = Runnable {
             setTextStyle(
                 weight = if (isDozing()) dozingWeight else lockScreenWeight,
-                textSize = -1f,
                 color = null,
                 animate = true,
+                interpolator = null,
                 duration = CHARGE_ANIM_DURATION_PHASE_1,
                 delay = 0,
                 onAnimationEnd = null
@@ -395,9 +386,9 @@
         }
         setTextStyle(
             weight = if (isDozing()) lockScreenWeight else dozingWeight,
-            textSize = -1f,
             color = null,
             animate = true,
+            interpolator = null,
             duration = CHARGE_ANIM_DURATION_PHASE_0,
             delay = chargeAnimationDelay.toLong(),
             onAnimationEnd = startAnimPhase2
@@ -408,9 +399,9 @@
         logger.d("animateDoze")
         setTextStyle(
             weight = if (isDozing) dozingWeight else lockScreenWeight,
-            textSize = -1f,
             color = if (isDozing) dozingColor else lockScreenColor,
             animate = animate,
+            interpolator = null,
             duration = DOZE_ANIM_DURATION,
             delay = 0,
             onAnimationEnd = null
@@ -448,7 +439,6 @@
      */
     private fun setTextStyle(
         @IntRange(from = 0, to = 1000) weight: Int,
-        @FloatRange(from = 0.0) textSize: Float,
         color: Int?,
         animate: Boolean,
         interpolator: TimeInterpolator?,
@@ -459,7 +449,6 @@
         textAnimator?.let {
             it.setTextStyle(
                 weight = weight,
-                textSize = textSize,
                 color = color,
                 animate = animate && isAnimationEnabled,
                 duration = duration,
@@ -474,7 +463,6 @@
                 onTextAnimatorInitialized = { textAnimator ->
                     textAnimator.setTextStyle(
                         weight = weight,
-                        textSize = textSize,
                         color = color,
                         animate = false,
                         duration = duration,
@@ -487,27 +475,6 @@
             }
     }
 
-    private fun setTextStyle(
-        @IntRange(from = 0, to = 1000) weight: Int,
-        @FloatRange(from = 0.0) textSize: Float,
-        color: Int?,
-        animate: Boolean,
-        duration: Long,
-        delay: Long,
-        onAnimationEnd: Runnable?
-    ) {
-        setTextStyle(
-            weight = weight,
-            textSize = textSize,
-            color = color,
-            animate = animate,
-            interpolator = null,
-            duration = duration,
-            delay = delay,
-            onAnimationEnd = onAnimationEnd
-        )
-    }
-
     fun refreshFormat() = refreshFormat(DateFormat.is24HourFormat(context))
     fun refreshFormat(use24HourFormat: Boolean) {
         Patterns.update(context)
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 b392014..502dbe3 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
@@ -239,6 +239,8 @@
     }
 
     inner class DefaultClockEvents : ClockEvents {
+        override var isReactiveTouchInteractionEnabled: Boolean = false
+
         override fun onTimeFormatChanged(is24Hr: Boolean) =
             clocks.forEach { it.refreshFormat(is24Hr) }
 
diff --git a/packages/SystemUI/customization/src/com/android/systemui/util/Assert.java b/packages/SystemUI/customization/src/com/android/systemui/util/Assert.java
index 165e972..de9baa5 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/util/Assert.java
+++ b/packages/SystemUI/customization/src/com/android/systemui/util/Assert.java
@@ -79,6 +79,21 @@
         }
     }
 
+    /**
+     * Asserts that the current thread is the same as the given thread, or that the current thread
+     * is the test thread.
+     * @param expected The looper we expected to be running on
+     */
+    public static void isCurrentThread(Looper expected) {
+        if (!expected.isCurrentThread()
+                && (sTestThread == null || sTestThread != Thread.currentThread())) {
+            throw new IllegalStateException("Called on wrong thread thread."
+                    + " wanted " + expected.getThread().getName()
+                    + " but instead got Thread.currentThread()="
+                    + Thread.currentThread().getName());
+        }
+    }
+
     public static void isNotMainThread() {
         if (sMainLooper.isCurrentThread()
                 && (sTestThread == null || sTestThread == Thread.currentThread())) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index 256687b..89bafb9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -16,6 +16,12 @@
 
 package com.android.systemui.bouncer.ui.viewmodel
 
+import android.view.KeyEvent.KEYCODE_0
+import android.view.KeyEvent.KEYCODE_4
+import android.view.KeyEvent.KEYCODE_A
+import android.view.KeyEvent.KEYCODE_DEL
+import android.view.KeyEvent.KEYCODE_NUMPAD_0
+import androidx.compose.ui.input.key.KeyEventType
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.SceneKey
@@ -34,6 +40,8 @@
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
+import kotlin.random.Random
+import kotlin.random.nextInt
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.asStateFlow
@@ -444,6 +452,44 @@
             assertThat(pin).hasSize(FakeAuthenticationRepository.HINTING_PIN_LENGTH + 1)
         }
 
+    @Test
+    fun onKeyboardInput_pinInput_isUpdated() =
+        testScope.runTest {
+            val pin by collectLastValue(underTest.pinInput.map { it.getPin() })
+            lockDeviceAndOpenPinBouncer()
+            val random = Random(System.currentTimeMillis())
+            // Generate a random 4 digit PIN
+            val expectedPin =
+                with(random) { arrayOf(nextInt(0..9), nextInt(0..9), nextInt(0..9), nextInt(0..9)) }
+
+            // Enter the PIN using NUM pad and normal number keyboard events
+            underTest.onKeyEvent(KeyEventType.KeyDown, KEYCODE_0 + expectedPin[0])
+            underTest.onKeyEvent(KeyEventType.KeyUp, KEYCODE_0 + expectedPin[0])
+
+            underTest.onKeyEvent(KeyEventType.KeyDown, KEYCODE_NUMPAD_0 + expectedPin[1])
+            underTest.onKeyEvent(KeyEventType.KeyUp, KEYCODE_NUMPAD_0 + expectedPin[1])
+
+            underTest.onKeyEvent(KeyEventType.KeyDown, KEYCODE_0 + expectedPin[2])
+            underTest.onKeyEvent(KeyEventType.KeyUp, KEYCODE_0 + expectedPin[2])
+
+            // Enter an additional digit in between and delete it
+            underTest.onKeyEvent(KeyEventType.KeyDown, KEYCODE_4)
+            underTest.onKeyEvent(KeyEventType.KeyUp, KEYCODE_4)
+
+            // Delete that additional digit
+            underTest.onKeyEvent(KeyEventType.KeyDown, KEYCODE_DEL)
+            underTest.onKeyEvent(KeyEventType.KeyUp, KEYCODE_DEL)
+
+            // Try entering a non digit character, this should be ignored.
+            underTest.onKeyEvent(KeyEventType.KeyDown, KEYCODE_A)
+            underTest.onKeyEvent(KeyEventType.KeyUp, KEYCODE_A)
+
+            underTest.onKeyEvent(KeyEventType.KeyDown, KEYCODE_NUMPAD_0 + expectedPin[3])
+            underTest.onKeyEvent(KeyEventType.KeyUp, KEYCODE_NUMPAD_0 + expectedPin[3])
+
+            assertThat(pin).containsExactly(*expectedPin)
+        }
+
     private fun TestScope.switchToScene(toScene: SceneKey) {
         val currentScene by collectLastValue(sceneInteractor.currentScene)
         val bouncerHidden = currentScene == Scenes.Bouncer && toScene != Scenes.Bouncer
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/ScreenBrightnessDisplayManagerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/ScreenBrightnessDisplayManagerRepositoryTest.kt
index e39ad4f..a676c7d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/ScreenBrightnessDisplayManagerRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/data/repository/ScreenBrightnessDisplayManagerRepositoryTest.kt
@@ -25,15 +25,18 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.brightness.data.model.LinearBrightness
+import com.android.systemui.brightness.shared.model.LinearBrightness
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.core.FakeLogBuffer
+import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.argumentCaptor
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -74,6 +77,8 @@
             ScreenBrightnessDisplayManagerRepository(
                 displayId,
                 displayManager,
+                FakeLogBuffer.Factory.create(),
+                mock<TableLogBuffer>(),
                 kosmos.applicationCoroutineScope,
                 kosmos.testDispatcher,
             )
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorTest.kt
index 33c44f8..b6616bf 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorTest.kt
@@ -20,13 +20,16 @@
 import androidx.test.filters.SmallTest
 import com.android.settingslib.display.BrightnessUtils
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.brightness.data.model.LinearBrightness
 import com.android.systemui.brightness.data.repository.fakeScreenBrightnessRepository
 import com.android.systemui.brightness.data.repository.screenBrightnessRepository
-import com.android.systemui.brightness.shared.GammaBrightness
+import com.android.systemui.brightness.shared.model.GammaBrightness
+import com.android.systemui.brightness.shared.model.LinearBrightness
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.mock
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
@@ -41,7 +44,14 @@
 
     private val kosmos = testKosmos()
 
-    private val underTest = ScreenBrightnessInteractor(kosmos.screenBrightnessRepository)
+    private val underTest =
+        with(kosmos) {
+            ScreenBrightnessInteractor(
+                screenBrightnessRepository,
+                applicationCoroutineScope,
+                mock<TableLogBuffer>()
+            )
+        }
 
     @Test
     fun gammaBrightness() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt
index 0058ee4..8402676 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelTest.kt
@@ -20,15 +20,16 @@
 import androidx.test.filters.SmallTest
 import com.android.settingslib.display.BrightnessUtils
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.brightness.data.model.LinearBrightness
 import com.android.systemui.brightness.data.repository.fakeScreenBrightnessRepository
 import com.android.systemui.brightness.domain.interactor.brightnessPolicyEnforcementInteractor
 import com.android.systemui.brightness.domain.interactor.screenBrightnessInteractor
-import com.android.systemui.brightness.shared.GammaBrightness
+import com.android.systemui.brightness.shared.model.GammaBrightness
+import com.android.systemui.brightness.shared.model.LinearBrightness
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.res.R
 import com.android.systemui.testKosmos
@@ -52,6 +53,7 @@
             BrightnessSliderViewModel(
                 screenBrightnessInteractor,
                 brightnessPolicyEnforcementInteractor,
+                applicationCoroutineScope,
             )
         }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
index 0ab0959..e61b2d0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt
@@ -21,6 +21,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
 import com.android.systemui.communal.domain.interactor.setCommunalAvailable
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.coroutines.collectLastValue
@@ -35,6 +36,8 @@
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.statusbar.notificationShadeWindowController
+import com.android.systemui.statusbar.phone.centralSurfaces
+import com.android.systemui.statusbar.phone.centralSurfacesOptional
 import com.android.systemui.testKosmos
 import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
@@ -49,6 +52,7 @@
 import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.whenever
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -67,6 +71,7 @@
                 CommunalSceneStartable(
                         dockManager = dockManager,
                         communalInteractor = communalInteractor,
+                        communalSceneInteractor = communalSceneInteractor,
                         keyguardTransitionInteractor = keyguardTransitionInteractor,
                         keyguardInteractor = keyguardInteractor,
                         systemSettings = fakeSettings,
@@ -74,6 +79,7 @@
                         applicationScope = applicationCoroutineScope,
                         bgScope = applicationCoroutineScope,
                         mainDispatcher = testDispatcher,
+                        centralSurfacesOpt = centralSurfacesOptional,
                     )
                     .apply { start() }
 
@@ -90,9 +96,9 @@
     fun keyguardGoesAway_forceBlankScene() =
         with(kosmos) {
             testScope.runTest {
-                val scene by collectLastValue(communalInteractor.desiredScene)
+                val scene by collectLastValue(communalSceneInteractor.currentScene)
 
-                communalInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal)
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
                 fakeKeyguardTransitionRepository.sendTransitionSteps(
@@ -110,7 +116,7 @@
     fun deviceDocked_forceCommunalScene() =
         with(kosmos) {
             testScope.runTest {
-                val scene by collectLastValue(communalInteractor.desiredScene)
+                val scene by collectLastValue(communalSceneInteractor.currentScene)
                 assertThat(scene).isEqualTo(CommunalScenes.Blank)
 
                 updateDocked(true)
@@ -127,8 +133,9 @@
     fun occluded_forceBlankScene() =
         with(kosmos) {
             testScope.runTest {
-                val scene by collectLastValue(communalInteractor.desiredScene)
-                communalInteractor.changeScene(CommunalScenes.Communal)
+                whenever(centralSurfaces.isLaunchingActivityOverLockscreen).thenReturn(false)
+                val scene by collectLastValue(communalSceneInteractor.currentScene)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal)
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
                 updateDocked(true)
@@ -142,10 +149,29 @@
         }
 
     @Test
+    fun occluded_doesNotForceBlankSceneIfLaunchingActivityOverLockscreen() =
+        with(kosmos) {
+            testScope.runTest {
+                whenever(centralSurfaces.isLaunchingActivityOverLockscreen).thenReturn(true)
+                val scene by collectLastValue(communalSceneInteractor.currentScene)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal)
+                assertThat(scene).isEqualTo(CommunalScenes.Communal)
+
+                updateDocked(true)
+                fakeKeyguardTransitionRepository.sendTransitionSteps(
+                    from = KeyguardState.GLANCEABLE_HUB,
+                    to = KeyguardState.OCCLUDED,
+                    testScope = this
+                )
+                assertThat(scene).isEqualTo(CommunalScenes.Communal)
+            }
+        }
+
+    @Test
     fun deviceDocked_doesNotForceCommunalIfTransitioningFromCommunal() =
         with(kosmos) {
             testScope.runTest {
-                val scene by collectLastValue(communalInteractor.desiredScene)
+                val scene by collectLastValue(communalSceneInteractor.currentScene)
                 assertThat(scene).isEqualTo(CommunalScenes.Blank)
 
                 updateDocked(true)
@@ -162,8 +188,8 @@
     fun deviceAsleep_forceBlankSceneAfterTimeout() =
         with(kosmos) {
             testScope.runTest {
-                val scene by collectLastValue(communalInteractor.desiredScene)
-                communalInteractor.changeScene(CommunalScenes.Communal)
+                val scene by collectLastValue(communalSceneInteractor.currentScene)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal)
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
                 fakeKeyguardTransitionRepository.sendTransitionSteps(
@@ -183,8 +209,8 @@
     fun deviceAsleep_wakesUpBeforeTimeout_noChangeInScene() =
         with(kosmos) {
             testScope.runTest {
-                val scene by collectLastValue(communalInteractor.desiredScene)
-                communalInteractor.changeScene(CommunalScenes.Communal)
+                val scene by collectLastValue(communalSceneInteractor.currentScene)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal)
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
                 fakeKeyguardTransitionRepository.sendTransitionSteps(
@@ -212,8 +238,8 @@
     fun dockingOnLockscreen_forcesCommunal() =
         with(kosmos) {
             testScope.runTest {
-                communalInteractor.changeScene(CommunalScenes.Blank)
-                val scene by collectLastValue(communalInteractor.desiredScene)
+                communalSceneInteractor.changeScene(CommunalScenes.Blank)
+                val scene by collectLastValue(communalSceneInteractor.currentScene)
 
                 // device is docked while on the lockscreen
                 fakeKeyguardTransitionRepository.sendTransitionSteps(
@@ -234,8 +260,8 @@
     fun dockingOnLockscreen_doesNotForceCommunalIfDreamStarts() =
         with(kosmos) {
             testScope.runTest {
-                communalInteractor.changeScene(CommunalScenes.Blank)
-                val scene by collectLastValue(communalInteractor.desiredScene)
+                communalSceneInteractor.changeScene(CommunalScenes.Blank)
+                val scene by collectLastValue(communalSceneInteractor.currentScene)
 
                 // device is docked while on the lockscreen
                 fakeKeyguardTransitionRepository.sendTransitionSteps(
@@ -266,9 +292,9 @@
             testScope.runTest {
                 // Device is dreaming and on communal.
                 updateDreaming(true)
-                communalInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal)
 
-                val scene by collectLastValue(communalInteractor.desiredScene)
+                val scene by collectLastValue(communalSceneInteractor.currentScene)
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
                 // Scene times out back to blank after the screen timeout.
@@ -283,11 +309,11 @@
             testScope.runTest {
                 // Device is not dreaming and on communal.
                 updateDreaming(false)
-                communalInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal)
 
                 // Scene stays as Communal
                 advanceTimeBy(SCREEN_TIMEOUT.milliseconds)
-                val scene by collectLastValue(communalInteractor.desiredScene)
+                val scene by collectLastValue(communalSceneInteractor.currentScene)
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
             }
         }
@@ -298,9 +324,9 @@
             testScope.runTest {
                 // Device is dreaming and on communal.
                 updateDreaming(true)
-                communalInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal)
 
-                val scene by collectLastValue(communalInteractor.desiredScene)
+                val scene by collectLastValue(communalSceneInteractor.currentScene)
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
                 // Wait a bit, but not long enough to timeout.
@@ -321,9 +347,9 @@
             testScope.runTest {
                 // Device is on communal, but not dreaming.
                 updateDreaming(false)
-                communalInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal)
 
-                val scene by collectLastValue(communalInteractor.desiredScene)
+                val scene by collectLastValue(communalSceneInteractor.currentScene)
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
                 // Wait a bit, but not long enough to timeout, then start dreaming.
@@ -342,11 +368,11 @@
         with(kosmos) {
             testScope.runTest {
                 // Device is on communal.
-                communalInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal)
 
                 // Device stays on the hub after the timeout since we're not dreaming.
                 advanceTimeBy(SCREEN_TIMEOUT.milliseconds * 2)
-                val scene by collectLastValue(communalInteractor.desiredScene)
+                val scene by collectLastValue(communalSceneInteractor.currentScene)
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
                 // Start dreaming.
@@ -363,9 +389,9 @@
             testScope.runTest {
                 // Device is dreaming and on communal.
                 updateDreaming(true)
-                communalInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal)
 
-                val scene by collectLastValue(communalInteractor.desiredScene)
+                val scene by collectLastValue(communalSceneInteractor.currentScene)
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
                 // Wait a bit, but not long enough to timeout.
@@ -392,9 +418,9 @@
 
                 // Device is dreaming and on communal.
                 updateDreaming(true)
-                communalInteractor.changeScene(CommunalScenes.Communal)
+                communalSceneInteractor.changeScene(CommunalScenes.Communal)
 
-                val scene by collectLastValue(communalInteractor.desiredScene)
+                val scene by collectLastValue(communalSceneInteractor.currentScene)
                 assertThat(scene).isEqualTo(CommunalScenes.Communal)
 
                 // Scene times out back to blank after the screen timeout.
@@ -421,12 +447,6 @@
             runCurrent()
         }
 
-    private suspend fun TestScope.enableCommunal() =
-        with(kosmos) {
-            setCommunalAvailable(true)
-            runCurrent()
-        }
-
     companion object {
         private const val SCREEN_TIMEOUT = 1000
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
index 45e7d8a..fd0bf4d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
@@ -41,6 +41,7 @@
     private val underTest by lazy {
         CommunalSceneRepositoryImpl(
             kosmos.applicationCoroutineScope,
+            kosmos.applicationCoroutineScope,
             kosmos.sceneDataSource,
         )
     }
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 89c5495..fb2b33d 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,6 +34,8 @@
 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.shared.model.CommunalBackgroundType
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
 import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -43,6 +45,7 @@
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.settings.fakeSettings
 import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -216,6 +219,32 @@
                 )
         }
 
+    @Test
+    fun backgroundType_defaultValue() =
+        testScope.runTest {
+            val backgroundType by collectLastValue(underTest.getBackground(PRIMARY_USER))
+            assertThat(backgroundType).isEqualTo(CommunalBackgroundType.DEFAULT)
+        }
+
+    @Test
+    fun backgroundType_verifyAllValues() =
+        testScope.runTest {
+            val backgroundType by collectLastValue(underTest.getBackground(PRIMARY_USER))
+            for (type in CommunalBackgroundType.entries) {
+                kosmos.fakeSettings.putIntForUser(
+                    GLANCEABLE_HUB_BACKGROUND_SETTING,
+                    type.value,
+                    PRIMARY_USER.id
+                )
+                assertWithMessage(
+                        "Expected $type when $GLANCEABLE_HUB_BACKGROUND_SETTING is set to" +
+                            " ${type.value} but was $backgroundType"
+                    )
+                    .that(backgroundType)
+                    .isEqualTo(type)
+            }
+        }
+
     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/CommunalInteractorCommunalDisabledTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorCommunalDisabledTest.kt
index 6b896c6..cebcbc9 100644
--- 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
@@ -23,7 +23,7 @@
 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.fakeCommunalRepository
+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
@@ -56,7 +56,7 @@
 
     @Before
     fun setUp() {
-        communalRepository = kosmos.fakeCommunalRepository
+        communalRepository = kosmos.fakeCommunalSceneRepository
         widgetRepository = kosmos.fakeCommunalWidgetRepository
         keyguardRepository = kosmos.fakeKeyguardRepository
 
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 12389a3..ffa63d8 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
@@ -44,7 +44,7 @@
 import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
 import com.android.systemui.communal.data.repository.fakeCommunalMediaRepository
 import com.android.systemui.communal.data.repository.fakeCommunalPrefsRepository
-import com.android.systemui.communal.data.repository.fakeCommunalRepository
+import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
 import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository
 import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
 import com.android.systemui.communal.domain.model.CommunalContentModel
@@ -132,7 +132,7 @@
         MockitoAnnotations.initMocks(this)
 
         tutorialRepository = kosmos.fakeCommunalTutorialRepository
-        communalRepository = kosmos.fakeCommunalRepository
+        communalRepository = kosmos.fakeCommunalSceneRepository
         mediaRepository = kosmos.fakeCommunalMediaRepository
         widgetRepository = kosmos.fakeCommunalWidgetRepository
         smartspaceRepository = kosmos.fakeSmartspaceRepository
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
new file mode 100644
index 0000000..aad2e60
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.communalSceneRepository
+import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class CommunalSceneInteractorTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    private val repository = kosmos.communalSceneRepository
+    private val underTest by lazy { kosmos.communalSceneInteractor }
+
+    @Test
+    fun changeScene() =
+        testScope.runTest {
+            val currentScene by collectLastValue(underTest.currentScene)
+            assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
+
+            underTest.changeScene(CommunalScenes.Communal)
+            assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
+        }
+
+    @Test
+    fun snapToScene() =
+        testScope.runTest {
+            val currentScene by collectLastValue(underTest.currentScene)
+            assertThat(currentScene).isEqualTo(CommunalScenes.Blank)
+
+            underTest.snapToScene(CommunalScenes.Communal)
+            assertThat(currentScene).isEqualTo(CommunalScenes.Communal)
+        }
+
+    @Test
+    fun transitionProgress_fullProgress() =
+        testScope.runTest {
+            val transitionProgress by
+                collectLastValue(underTest.transitionProgressToScene(CommunalScenes.Blank))
+            assertThat(transitionProgress)
+                .isEqualTo(CommunalTransitionProgressModel.Idle(CommunalScenes.Blank))
+
+            val transitionState =
+                MutableStateFlow<ObservableTransitionState>(
+                    ObservableTransitionState.Idle(CommunalScenes.Communal)
+                )
+            underTest.setTransitionState(transitionState)
+
+            assertThat(transitionProgress)
+                .isEqualTo(CommunalTransitionProgressModel.Idle(CommunalScenes.Communal))
+        }
+
+    @Test
+    fun transitionProgress_transitioningAwayFromTrackedScene() =
+        testScope.runTest {
+            val transitionProgress by
+                collectLastValue(underTest.transitionProgressToScene(CommunalScenes.Blank))
+
+            val progress = MutableStateFlow(0f)
+            underTest.setTransitionState(
+                MutableStateFlow(
+                    ObservableTransitionState.Transition(
+                        fromScene = CommunalScenes.Blank,
+                        toScene = CommunalScenes.Communal,
+                        currentScene = flowOf(CommunalScenes.Communal),
+                        progress = progress,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            )
+
+            // Partially transition.
+            progress.value = .4f
+
+            // This is a transition we don't care about the progress of.
+            assertThat(transitionProgress)
+                .isEqualTo(CommunalTransitionProgressModel.OtherTransition)
+
+            // Transition is at full progress.
+            progress.value = 1f
+            assertThat(transitionProgress)
+                .isEqualTo(CommunalTransitionProgressModel.OtherTransition)
+
+            // Transition finishes.
+            underTest.setTransitionState(
+                MutableStateFlow(ObservableTransitionState.Idle(CommunalScenes.Communal))
+            )
+            assertThat(transitionProgress)
+                .isEqualTo(CommunalTransitionProgressModel.Idle(CommunalScenes.Communal))
+        }
+
+    @Test
+    fun transitionProgress_transitioningToTrackedScene() =
+        testScope.runTest {
+            val transitionProgress by
+                collectLastValue(underTest.transitionProgressToScene(CommunalScenes.Communal))
+
+            val progress = MutableStateFlow(0f)
+            underTest.setTransitionState(
+                MutableStateFlow(
+                    ObservableTransitionState.Transition(
+                        fromScene = CommunalScenes.Blank,
+                        toScene = CommunalScenes.Communal,
+                        currentScene = flowOf(CommunalScenes.Communal),
+                        progress = progress,
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+            )
+
+            // Partially transition.
+            progress.value = .4f
+            assertThat(transitionProgress)
+                .isEqualTo(CommunalTransitionProgressModel.Transition(0.4f))
+
+            // Transition is at full progress.
+            progress.value = 1f
+            assertThat(transitionProgress).isEqualTo(CommunalTransitionProgressModel.Transition(1f))
+
+            // Transition finishes.
+            underTest.setTransitionState(
+                MutableStateFlow(ObservableTransitionState.Idle(CommunalScenes.Communal))
+            )
+            assertThat(transitionProgress)
+                .isEqualTo(CommunalTransitionProgressModel.Idle(CommunalScenes.Communal))
+        }
+
+    @Test
+    fun isIdleOnCommunal() =
+        testScope.runTest {
+            // isIdleOnCommunal is false when not on communal.
+            val isIdleOnCommunal by collectLastValue(underTest.isIdleOnCommunal)
+            assertThat(isIdleOnCommunal).isEqualTo(false)
+
+            val transitionState: MutableStateFlow<ObservableTransitionState> =
+                MutableStateFlow(ObservableTransitionState.Idle(CommunalScenes.Communal))
+
+            // Transition to communal.
+            repository.setTransitionState(transitionState)
+            assertThat(isIdleOnCommunal).isEqualTo(true)
+
+            // Start transition away from communal.
+            transitionState.value =
+                ObservableTransitionState.Transition(
+                    fromScene = CommunalScenes.Communal,
+                    toScene = CommunalScenes.Blank,
+                    currentScene = flowOf(CommunalScenes.Blank),
+                    progress = flowOf(0f),
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
+                )
+            assertThat(isIdleOnCommunal).isEqualTo(false)
+        }
+
+    @Test
+    fun isCommunalVisible() =
+        testScope.runTest {
+            // isCommunalVisible is false when not on communal.
+            val isCommunalVisible by collectLastValue(underTest.isCommunalVisible)
+            assertThat(isCommunalVisible).isEqualTo(false)
+
+            val transitionState: MutableStateFlow<ObservableTransitionState> =
+                MutableStateFlow(
+                    ObservableTransitionState.Transition(
+                        fromScene = CommunalScenes.Blank,
+                        toScene = CommunalScenes.Communal,
+                        currentScene = flowOf(CommunalScenes.Communal),
+                        progress = flowOf(0f),
+                        isInitiatedByUserInput = false,
+                        isUserInputOngoing = flowOf(false),
+                    )
+                )
+
+            // Start transition to communal.
+            repository.setTransitionState(transitionState)
+            assertThat(isCommunalVisible).isEqualTo(true)
+
+            // Finish transition to communal
+            transitionState.value = ObservableTransitionState.Idle(CommunalScenes.Communal)
+            assertThat(isCommunalVisible).isEqualTo(true)
+
+            // Start transition away from communal.
+            transitionState.value =
+                ObservableTransitionState.Transition(
+                    fromScene = CommunalScenes.Communal,
+                    toScene = CommunalScenes.Blank,
+                    currentScene = flowOf(CommunalScenes.Blank),
+                    progress = flowOf(1.0f),
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
+                )
+            assertThat(isCommunalVisible).isEqualTo(true)
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/compose/CommunalHubUtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/compose/CommunalHubUtilsTest.kt
new file mode 100644
index 0000000..643063e7
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/compose/CommunalHubUtilsTest.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.communal.ui.compose
+
+import android.testing.TestableLooper
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.layout.LayoutCoordinates
+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.mockito.kotlin.mock
+
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class CommunalHubUtilsTest : SysuiTestCase() {
+    @Test
+    fun isPointerWithinEnabledRemoveButton_ensureDisabledStatePriority() {
+        assertThat(
+                isPointerWithinEnabledRemoveButton(false, mock<Offset>(), mock<LayoutCoordinates>())
+            )
+            .isFalse()
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index 6ca04df..84dbfd4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -39,6 +39,7 @@
 import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository
 import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
 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.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.log.CommunalUiEvent
@@ -106,6 +107,7 @@
 
         underTest =
             CommunalEditModeViewModel(
+                kosmos.communalSceneInteractor,
                 kosmos.communalInteractor,
                 kosmos.communalSettingsInteractor,
                 mediaHost,
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 6b80775..9dcea82 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
@@ -32,10 +32,12 @@
 import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
 import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
 import com.android.systemui.communal.data.repository.fakeCommunalMediaRepository
-import com.android.systemui.communal.data.repository.fakeCommunalRepository
+import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
 import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository
 import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
 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.domain.interactor.communalTutorialInteractor
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.model.CommunalScenes
@@ -53,6 +55,8 @@
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.DozeTransitionModel
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.TransitionState
@@ -61,6 +65,8 @@
 import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager
 import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
+import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.shade.ShadeTestUtil
 import com.android.systemui.shade.domain.interactor.shadeInteractor
@@ -70,7 +76,6 @@
 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.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.flowOf
@@ -84,6 +89,7 @@
 import org.mockito.Mockito
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4
 import platform.test.runner.parameterized.Parameters
 
@@ -126,7 +132,7 @@
         mediaRepository = kosmos.fakeCommunalMediaRepository
         userRepository = kosmos.fakeUserRepository
         shadeTestUtil = kosmos.shadeTestUtil
-        communalRepository = kosmos.fakeCommunalRepository
+        communalRepository = kosmos.fakeCommunalSceneRepository
 
         kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
         mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
@@ -137,13 +143,17 @@
         )
         whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id))
 
+        kosmos.powerInteractor.setAwakeForTest()
+
         underTest =
             CommunalViewModel(
                 testScope,
                 context.resources,
                 kosmos.keyguardTransitionInteractor,
                 kosmos.keyguardInteractor,
+                kosmos.communalSceneInteractor,
                 kosmos.communalInteractor,
+                kosmos.communalSettingsInteractor,
                 kosmos.communalTutorialInteractor,
                 kosmos.shadeInteractor,
                 mediaHost,
@@ -466,6 +476,229 @@
             assertThat(isFocusable).isEqualTo(false)
         }
 
+    @Test
+    fun isCommunalContentFlowFrozen_whenActivityStartedWhileDreaming() =
+        testScope.runTest {
+            val isCommunalContentFlowFrozen by
+                collectLastValue(underTest.isCommunalContentFlowFrozen)
+
+            // 1. When dreaming not dozing
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+            )
+            keyguardRepository.setDreaming(true)
+            keyguardRepository.setDreamingWithOverlay(true)
+            advanceTimeBy(60L)
+            // And keyguard is occluded by dream
+            keyguardRepository.setKeyguardOccluded(true)
+
+            // And on hub
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.DREAMING,
+                to = KeyguardState.GLANCEABLE_HUB,
+                testScope = testScope,
+            )
+
+            // Then flow is not frozen
+            assertThat(isCommunalContentFlowFrozen).isEqualTo(false)
+
+            // 2. When dreaming stopped by the new activity about to show on lock screen
+            keyguardRepository.setDreamingWithOverlay(false)
+            advanceTimeBy(60L)
+
+            // Then flow is frozen
+            assertThat(isCommunalContentFlowFrozen).isEqualTo(true)
+
+            // 3. When transitioned to OCCLUDED and activity shows
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.GLANCEABLE_HUB,
+                to = KeyguardState.OCCLUDED,
+                testScope = testScope,
+            )
+
+            // Then flow is not frozen
+            assertThat(isCommunalContentFlowFrozen).isEqualTo(false)
+        }
+
+    @Test
+    fun isCommunalContentFlowFrozen_whenActivityStartedInHandheldMode() =
+        testScope.runTest {
+            val isCommunalContentFlowFrozen by
+                collectLastValue(underTest.isCommunalContentFlowFrozen)
+
+            // 1. When on keyguard and not occluded
+            keyguardRepository.setKeyguardShowing(true)
+            keyguardRepository.setKeyguardOccluded(false)
+
+            // And transitioned to hub
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.GLANCEABLE_HUB,
+                testScope = testScope,
+            )
+
+            // Then flow is not frozen
+            assertThat(isCommunalContentFlowFrozen).isEqualTo(false)
+
+            // 2. When occluded by a new activity
+            keyguardRepository.setKeyguardOccluded(true)
+            runCurrent()
+
+            // And transitioning to occluded
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(
+                    from = KeyguardState.GLANCEABLE_HUB,
+                    to = KeyguardState.OCCLUDED,
+                    transitionState = TransitionState.STARTED,
+                )
+            )
+
+            keyguardTransitionRepository.sendTransitionStep(
+                from = KeyguardState.GLANCEABLE_HUB,
+                to = KeyguardState.OCCLUDED,
+                transitionState = TransitionState.RUNNING,
+                value = 0.5f,
+            )
+
+            // Then flow is frozen
+            assertThat(isCommunalContentFlowFrozen).isEqualTo(true)
+
+            // 3. When transition is finished
+            keyguardTransitionRepository.sendTransitionStep(
+                from = KeyguardState.GLANCEABLE_HUB,
+                to = KeyguardState.OCCLUDED,
+                transitionState = TransitionState.FINISHED,
+                value = 1f,
+            )
+
+            // Then flow is not frozen
+            assertThat(isCommunalContentFlowFrozen).isEqualTo(false)
+        }
+
+    @Test
+    fun communalContent_emitsFrozenContent_whenFrozen() =
+        testScope.runTest {
+            val communalContent by collectLastValue(underTest.communalContent)
+            tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
+
+            // When dreaming
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+            )
+            keyguardRepository.setDreaming(true)
+            keyguardRepository.setDreamingWithOverlay(true)
+            advanceTimeBy(60L)
+            keyguardRepository.setKeyguardOccluded(true)
+
+            // And transitioned to hub
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.DREAMING,
+                to = KeyguardState.GLANCEABLE_HUB,
+                testScope = testScope,
+            )
+
+            // Widgets available
+            val widgets =
+                listOf(
+                    CommunalWidgetContentModel.Available(
+                        appWidgetId = 0,
+                        priority = 30,
+                        providerInfo = providerInfo,
+                    ),
+                    CommunalWidgetContentModel.Available(
+                        appWidgetId = 1,
+                        priority = 20,
+                        providerInfo = providerInfo,
+                    ),
+                )
+            widgetRepository.setCommunalWidgets(widgets)
+
+            // Then hub shows widgets and the CTA tile
+            assertThat(communalContent).hasSize(3)
+
+            // When dreaming stopped by another activity which should freeze flow
+            keyguardRepository.setDreamingWithOverlay(false)
+            advanceTimeBy(60L)
+
+            // New timer available
+            val target = Mockito.mock(SmartspaceTarget::class.java)
+            whenever<String?>(target.smartspaceTargetId).thenReturn("target")
+            whenever(target.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
+            whenever(target.remoteViews).thenReturn(Mockito.mock(RemoteViews::class.java))
+            smartspaceRepository.setCommunalSmartspaceTargets(listOf(target))
+            runCurrent()
+
+            // Still only emits widgets and the CTA tile
+            assertThat(communalContent).hasSize(3)
+            assertThat(communalContent?.get(0))
+                .isInstanceOf(CommunalContentModel.WidgetContent::class.java)
+            assertThat(communalContent?.get(1))
+                .isInstanceOf(CommunalContentModel.WidgetContent::class.java)
+            assertThat(communalContent?.get(2))
+                .isInstanceOf(CommunalContentModel.CtaTileInViewMode::class.java)
+        }
+
+    @Test
+    fun communalContent_emitsLatestContent_whenNotFrozen() =
+        testScope.runTest {
+            val communalContent by collectLastValue(underTest.communalContent)
+            tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED)
+
+            // When dreaming
+            keyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
+            )
+            keyguardRepository.setDreaming(true)
+            keyguardRepository.setDreamingWithOverlay(true)
+            advanceTimeBy(60L)
+            keyguardRepository.setKeyguardOccluded(true)
+
+            // Transitioned to Glanceable hub.
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.DREAMING,
+                to = KeyguardState.GLANCEABLE_HUB,
+                testScope = testScope,
+            )
+
+            // And widgets available
+            val widgets =
+                listOf(
+                    CommunalWidgetContentModel.Available(
+                        appWidgetId = 0,
+                        priority = 30,
+                        providerInfo = providerInfo,
+                    ),
+                    CommunalWidgetContentModel.Available(
+                        appWidgetId = 1,
+                        priority = 20,
+                        providerInfo = providerInfo,
+                    ),
+                )
+            widgetRepository.setCommunalWidgets(widgets)
+
+            // Then emits widgets and the CTA tile
+            assertThat(communalContent).hasSize(3)
+
+            // When new timer available
+            val target = Mockito.mock(SmartspaceTarget::class.java)
+            whenever(target.smartspaceTargetId).thenReturn("target")
+            whenever(target.featureType).thenReturn(SmartspaceTarget.FEATURE_TIMER)
+            whenever(target.remoteViews).thenReturn(Mockito.mock(RemoteViews::class.java))
+            smartspaceRepository.setCommunalSmartspaceTargets(listOf(target))
+            runCurrent()
+
+            // Then emits timer, widgets and the CTA tile
+            assertThat(communalContent).hasSize(4)
+            assertThat(communalContent?.get(0))
+                .isInstanceOf(CommunalContentModel.Smartspace::class.java)
+            assertThat(communalContent?.get(1))
+                .isInstanceOf(CommunalContentModel.WidgetContent::class.java)
+            assertThat(communalContent?.get(2))
+                .isInstanceOf(CommunalContentModel.WidgetContent::class.java)
+            assertThat(communalContent?.get(3))
+                .isInstanceOf(CommunalContentModel.CtaTileInViewMode::class.java)
+        }
+
     private suspend fun setIsMainUser(isMainUser: Boolean) {
         whenever(user.isMain).thenReturn(isMainUser)
         userRepository.setUserInfos(listOf(user))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
index b4f87c4..420b11c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
@@ -72,6 +72,7 @@
         verify(activityStarter)
             .startPendingIntentMaybeDismissingKeyguard(
                 eq(testIntent),
+                eq(false),
                 isNull(),
                 notNull(),
                 refEq(fillInIntent),
@@ -91,6 +92,7 @@
         verify(activityStarter)
             .startPendingIntentMaybeDismissingKeyguard(
                 eq(testIntent),
+                eq(false),
                 isNull(),
                 isNull(),
                 refEq(fillInIntent),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
index 933f095..ee8a22c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt
@@ -46,7 +46,7 @@
 import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
 import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository
-import com.android.systemui.communal.data.repository.fakeCommunalRepository
+import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.domain.interactor.communalInteractor
 import com.android.systemui.communal.domain.interactor.setCommunalAvailable
@@ -166,7 +166,7 @@
 
         lifecycleRegistry = FakeLifecycleRegistry(mLifecycleOwner)
         bouncerRepository = kosmos.fakeKeyguardBouncerRepository
-        communalRepository = kosmos.fakeCommunalRepository
+        communalRepository = kosmos.fakeCommunalSceneRepository
 
         whenever(mDreamOverlayComponent.getDreamOverlayContainerViewController())
             .thenReturn(mDreamOverlayContainerViewController)
@@ -397,6 +397,9 @@
         verify(mStateController).setOverlayActive(false)
         verify(mStateController).setLowLightActive(false)
         verify(mStateController).setEntryAnimationsFinished(false)
+
+        // Verify touch monitor destroyed
+        verify(mTouchMonitor).destroy()
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamServiceTest.kt
index 723f6a2..9300db9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamServiceTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamServiceTest.kt
@@ -16,29 +16,40 @@
 package com.android.systemui.dreams.homecontrols
 
 import android.app.Activity
+import android.content.Intent
+import android.service.controls.ControlsProviderService.CONTROLS_SURFACE_ACTIVITY_PANEL
+import android.service.controls.ControlsProviderService.CONTROLS_SURFACE_DREAM
+import android.service.controls.ControlsProviderService.EXTRA_CONTROLS_SURFACE
+import android.window.TaskFragmentInfo
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.controls.settings.FakeControlsSettingsRepository
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.log.core.FakeLogBuffer.Factory.Companion.create
 import com.android.systemui.log.logcatLogBuffer
 import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.whenever
 import com.android.systemui.util.wakelock.WakeLockFake
 import com.google.common.truth.Truth.assertThat
 import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.advanceUntilIdle
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mock
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.argThat
+import org.mockito.kotlin.argumentCaptor
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
 
+@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class HomeControlsDreamServiceTest : SysuiTestCase() {
@@ -46,31 +57,38 @@
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
 
-    private lateinit var fakeWakeLockBuilder: WakeLockFake.Builder
-    private lateinit var fakeWakeLock: WakeLockFake
+    private val fakeWakeLock = WakeLockFake()
+    private val fakeWakeLockBuilder by lazy {
+        WakeLockFake.Builder(context).apply { setWakeLock(fakeWakeLock) }
+    }
 
-    @Mock private lateinit var taskFragmentComponentFactory: TaskFragmentComponent.Factory
-    @Mock private lateinit var taskFragmentComponent: TaskFragmentComponent
-    @Mock private lateinit var activity: Activity
+    private val taskFragmentComponent = mock<TaskFragmentComponent>()
+    private val activity = mock<Activity>()
+    private val onCreateCallback = argumentCaptor<(TaskFragmentInfo) -> Unit>()
+    private val onInfoChangedCallback = argumentCaptor<(TaskFragmentInfo) -> Unit>()
+    private val hideCallback = argumentCaptor<() -> Unit>()
+    private val dreamServiceDelegate =
+        mock<DreamServiceDelegate> { on { getActivity(any()) } doReturn activity }
 
-    private lateinit var underTest: HomeControlsDreamService
+    private val taskFragmentComponentFactory =
+        mock<TaskFragmentComponent.Factory> {
+            on {
+                create(
+                    activity = eq(activity),
+                    onCreateCallback = onCreateCallback.capture(),
+                    onInfoChangedCallback = onInfoChangedCallback.capture(),
+                    hide = hideCallback.capture(),
+                )
+            } doReturn taskFragmentComponent
+        }
+
+    private val underTest: HomeControlsDreamService by lazy { buildService() }
 
     @Before
-    fun setup() =
-        with(kosmos) {
-            MockitoAnnotations.initMocks(this@HomeControlsDreamServiceTest)
-            whenever(taskFragmentComponentFactory.create(any(), any(), any(), any()))
-                .thenReturn(taskFragmentComponent)
-
-            fakeWakeLock = WakeLockFake()
-            fakeWakeLockBuilder = WakeLockFake.Builder(context)
-            fakeWakeLockBuilder.setWakeLock(fakeWakeLock)
-
-            whenever(controlsComponent.getControlsListingController())
-                .thenReturn(Optional.of(controlsListingController))
-
-            underTest = buildService { activity }
-        }
+    fun setup() {
+        whenever(kosmos.controlsComponent.getControlsListingController())
+            .thenReturn(Optional.of(kosmos.controlsListingController))
+    }
 
     @Test
     fun testOnAttachedToWindowCreatesTaskFragmentComponent() =
@@ -90,9 +108,12 @@
     @Test
     fun testNotCreatingTaskFragmentComponentWhenActivityIsNull() =
         testScope.runTest {
-            underTest = buildService { null }
+            val serviceWithNullActivity =
+                buildService(
+                    mock<DreamServiceDelegate> { on { getActivity(underTest) } doReturn null }
+                )
 
-            underTest.onAttachedToWindow()
+            serviceWithNullActivity.onAttachedToWindow()
             verify(taskFragmentComponentFactory, never()).create(any(), any(), any(), any())
         }
 
@@ -102,6 +123,7 @@
             underTest.onAttachedToWindow()
             assertThat(fakeWakeLock.isHeld).isTrue()
         }
+
     @Test
     fun testDetachWindow_wakeLockCanBeReleased() =
         testScope.runTest {
@@ -112,14 +134,60 @@
             assertThat(fakeWakeLock.isHeld).isFalse()
         }
 
-    private fun buildService(activityProvider: DreamActivityProvider): HomeControlsDreamService =
+    @Test
+    fun testFinishesDreamWithoutRestartingActivityWhenNotRedirectingWakes() =
+        testScope.runTest {
+            whenever(dreamServiceDelegate.redirectWake(any())).thenReturn(false)
+            underTest.onAttachedToWindow()
+            onCreateCallback.firstValue.invoke(mock<TaskFragmentInfo>())
+            verify(taskFragmentComponent, times(1)).startActivityInTaskFragment(intentMatcher())
+
+            // Task fragment becomes empty
+            onInfoChangedCallback.firstValue.invoke(
+                mock<TaskFragmentInfo> { on { isEmpty } doReturn true }
+            )
+            advanceUntilIdle()
+            // Dream is finished and activity is not restarted
+            verify(taskFragmentComponent, times(1)).startActivityInTaskFragment(intentMatcher())
+            verify(dreamServiceDelegate, never()).wakeUp(any())
+            verify(dreamServiceDelegate).finish(any())
+        }
+
+    @Test
+    fun testRestartsActivityWhenRedirectingWakes() =
+        testScope.runTest {
+            whenever(dreamServiceDelegate.redirectWake(any())).thenReturn(true)
+            underTest.onAttachedToWindow()
+            onCreateCallback.firstValue.invoke(mock<TaskFragmentInfo>())
+            verify(taskFragmentComponent, times(1)).startActivityInTaskFragment(intentMatcher())
+
+            // Task fragment becomes empty
+            onInfoChangedCallback.firstValue.invoke(
+                mock<TaskFragmentInfo> { on { isEmpty } doReturn true }
+            )
+            advanceUntilIdle()
+            // Activity is restarted instead of finishing the dream.
+            verify(taskFragmentComponent, times(2)).startActivityInTaskFragment(intentMatcher())
+            verify(dreamServiceDelegate).wakeUp(any())
+            verify(dreamServiceDelegate, never()).finish(any())
+        }
+
+    private fun intentMatcher() =
+        argThat<Intent> {
+            getIntExtra(EXTRA_CONTROLS_SURFACE, CONTROLS_SURFACE_ACTIVITY_PANEL) ==
+                CONTROLS_SURFACE_DREAM
+        }
+
+    private fun buildService(
+        activityProvider: DreamServiceDelegate = dreamServiceDelegate
+    ): HomeControlsDreamService =
         with(kosmos) {
             return HomeControlsDreamService(
                 controlsSettingsRepository = FakeControlsSettingsRepository(),
                 taskFragmentFactory = taskFragmentComponentFactory,
                 homeControlsComponentInteractor = homeControlsComponentInteractor,
-                fakeWakeLockBuilder,
-                dreamActivityProvider = activityProvider,
+                wakeLockBuilder = fakeWakeLockBuilder,
+                dreamServiceDelegate = activityProvider,
                 bgDispatcher = testDispatcher,
                 logBuffer = logcatLogBuffer("HomeControlsDreamServiceTest")
             )
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
index 29fbee0..7936ccc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/touch/CommunalTouchHandlerTest.java
@@ -108,7 +108,7 @@
         mTouchHandler.onSessionStart(mTouchSession);
         verify(mTouchSession).registerInputListener(inputEventListenerArgumentCaptor.capture());
         inputEventListenerArgumentCaptor.getValue().onInputEvent(motionEvent);
-        verify(mCentralSurfaces).handleDreamTouch(motionEvent);
+        verify(mCentralSurfaces).handleCommunalHubTouch(motionEvent);
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
index c51413a..4849e66 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/haptics/qs/QSLongPressEffectTest.kt
@@ -26,6 +26,7 @@
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.qsTileFactory
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.TestScope
@@ -41,6 +42,7 @@
 
     private val kosmos = testKosmos()
     private val vibratorHelper = kosmos.vibratorHelper
+    private val qsTile = kosmos.qsTileFactory.createTile("Test Tile")
 
     private val effectDuration = 400
     private val lowTickDuration = 12
@@ -61,6 +63,7 @@
                 vibratorHelper,
                 kosmos.keyguardInteractor,
             )
+        longPressEffect.qsTile = qsTile
     }
 
     @Test
@@ -91,8 +94,10 @@
         // GIVEN an action down event occurs
         longPressEffect.handleActionDown()
 
-        // THEN the effect moves to the TIMEOUT_WAIT state
+        // THEN the effect moves to the TIMEOUT_WAIT state and starts the wait
+        val action by collectLastValue(longPressEffect.actionType)
         assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.TIMEOUT_WAIT)
+        assertThat(action).isEqualTo(QSLongPressEffect.ActionType.WAIT_TAP_TIMEOUT)
     }
 
     @Test
@@ -107,20 +112,6 @@
         }
 
     @Test
-    fun onActionUp_whileWaiting_performsClick() =
-        testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) {
-            // GIVEN an action is being collected
-            val action by collectLastValue(longPressEffect.actionType)
-
-            // GIVEN an action up occurs
-            longPressEffect.handleActionUp()
-
-            // THEN the action to invoke is the click action and the effect does not start
-            assertThat(action).isEqualTo(QSLongPressEffect.ActionType.CLICK)
-            assertEffectDidNotStart()
-        }
-
-    @Test
     fun onWaitComplete_whileWaiting_beginsEffect() =
         testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) {
             // GIVEN the pressed timeout is complete
@@ -221,8 +212,10 @@
             // GIVEN that the animator was cancelled
             longPressEffect.handleAnimationCancel()
 
-            // THEN the state goes to the timeout wait
+            // THEN the state goes to the timeout wait and the wait is posted
+            val action by collectLastValue(longPressEffect.actionType)
             assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.TIMEOUT_WAIT)
+            assertThat(action).isEqualTo(QSLongPressEffect.ActionType.WAIT_TAP_TIMEOUT)
         }
 
     @Test
@@ -238,6 +231,29 @@
             assertThat(longPressEffect.state).isEqualTo(QSLongPressEffect.State.IDLE)
         }
 
+    @Test
+    fun onTileClick_whileWaiting_withQSTile_clicks() =
+        testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) {
+            // GIVEN that a click was detected
+            val couldClick = longPressEffect.onTileClick()
+
+            // THEN the click is successful
+            assertThat(couldClick).isTrue()
+        }
+
+    @Test
+    fun onTileClick_whileWaiting_withoutQSTile_cannotClick() =
+        testWhileInState(QSLongPressEffect.State.TIMEOUT_WAIT) {
+            // GIVEN that no QSTile has been set
+            longPressEffect.qsTile = null
+
+            // GIVEN that a click was detected
+            val couldClick = longPressEffect.onTileClick()
+
+            // THEN the click is not successful
+            assertThat(couldClick).isFalse()
+        }
+
     private fun testWithScope(initialize: Boolean = true, test: suspend TestScope.() -> Unit) =
         with(kosmos) {
             testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
index 26fcb23..49d0399 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLegacySettingSyncerTest.kt
@@ -90,6 +90,7 @@
                             .thenReturn(FakeSharedPreferences())
                     },
                 userTracker = FakeUserTracker(),
+                systemSettings = FakeSettings(),
                 broadcastDispatcher = fakeBroadcastDispatcher,
             )
         settings = FakeSettings()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
index 99a0185..9ab1ac1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManagerTest.kt
@@ -22,13 +22,14 @@
 import android.content.pm.UserInfo
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.backup.BackupHelper
+import com.android.systemui.res.R
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.util.FakeSharedPreferences
 import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.settings.FakeSettings
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -80,6 +81,7 @@
                 context = context,
                 userFileManager = userFileManager,
                 userTracker = userTracker,
+                systemSettings = FakeSettings(),
                 broadcastDispatcher = fakeBroadcastDispatcher,
             )
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
index 567e0a9..159ce36 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardQuickAffordanceRepositoryTest.kt
@@ -21,7 +21,6 @@
 import android.os.UserHandle
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig
@@ -32,6 +31,7 @@
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
 import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
 import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation
+import com.android.systemui.res.R
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.shared.customization.data.content.FakeCustomizationProviderClient
@@ -91,6 +91,7 @@
                             .thenReturn(FakeSharedPreferences())
                     },
                 userTracker = userTracker,
+                systemSettings = FakeSettings(),
                 broadcastDispatcher = fakeBroadcastDispatcher,
             )
         client1 = FakeCustomizationProviderClient()
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 d630a2f..6c5001a 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
@@ -136,20 +136,20 @@
 
     @Test
     @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
-    fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromAod_nonDismissableKeyguard() =
+    fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromAod_nonDismissibleKeyguard() =
         testScope.runTest {
             powerInteractor.onCameraLaunchGestureDetected()
             powerInteractor.setAwakeForTest()
             advanceTimeBy(100) // account for debouncing
 
-            // We should head back to GONE since we started there.
+            // We should head to OCCLUDED because keyguard is not dismissible.
             assertThat(transitionRepository)
                 .startedTransition(from = KeyguardState.AOD, to = KeyguardState.OCCLUDED)
         }
 
     @Test
     @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
-    fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromAod_dismissableKeyguard() =
+    fun testTransitionToGone_onWakeUp_ifPowerButtonGestureDetected_fromAod_dismissibleKeyguard() =
         testScope.runTest {
             kosmos.fakeKeyguardRepository.setKeyguardDismissible(true)
             powerInteractor.onCameraLaunchGestureDetected()
@@ -188,6 +188,7 @@
             )
 
             // Detect a power gesture and then wake up.
+            kosmos.fakeKeyguardRepository.setKeyguardDismissible(true)
             reset(transitionRepository)
             powerInteractor.onCameraLaunchGestureDetected()
             powerInteractor.setAwakeForTest()
@@ -355,6 +356,7 @@
             )
 
             // Detect a power gesture and then wake up.
+            kosmos.fakeKeyguardRepository.setKeyguardDismissible(true)
             reset(transitionRepository)
             powerInteractor.onCameraLaunchGestureDetected()
             powerInteractor.setAwakeForTest()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
index 593cfde..612f2e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromDozingTransitionInteractorTest.kt
@@ -39,7 +39,7 @@
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.data.repository.fakeCommunalRepository
+import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -56,13 +56,13 @@
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.testKosmos
 import junit.framework.Assert.assertEquals
-import kotlin.test.Test
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
+import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.spy
@@ -122,7 +122,7 @@
     @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun testTransitionToGlanceableHub_onWakeup_ifIdleOnCommunal_noOccludingActivity() =
         testScope.runTest {
-            kosmos.fakeCommunalRepository.setTransitionState(
+            kosmos.fakeCommunalSceneRepository.setTransitionState(
                 flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal))
             )
             runCurrent()
@@ -158,7 +158,7 @@
     @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun testTransitionToOccluded_onWakeup_whenOccludingActivityOnTop_evenIfIdleOnCommunal() =
         testScope.runTest {
-            kosmos.fakeCommunalRepository.setTransitionState(
+            kosmos.fakeCommunalSceneRepository.setTransitionState(
                 flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal))
             )
             runCurrent()
@@ -177,6 +177,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    @Suppress("ktlint:standard:max-line-length")
     fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetected_fromAod_nonDismissableKeyguard() =
         testScope.runTest {
             powerInteractor.onCameraLaunchGestureDetected()
@@ -229,6 +230,7 @@
             )
 
             // Detect a power gesture and then wake up.
+            kosmos.fakeKeyguardRepository.setKeyguardDismissible(true)
             reset(transitionRepository)
             powerInteractor.onCameraLaunchGestureDetected()
             powerInteractor.setAwakeForTest()
@@ -241,6 +243,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    @Suppress("ktlint:standard:max-line-length")
     fun testTransitionToOccluded_onWakeUp_ifPowerButtonGestureDetectedAfterFinishedInAod_fromGone() =
         testScope.runTest {
             powerInteractor.setAwakeForTest()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 2d77f4f..78a1167 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -148,6 +148,7 @@
                             .thenReturn(FakeSharedPreferences())
                     },
                 userTracker = userTracker,
+                systemSettings = FakeSettings(),
                 broadcastDispatcher = fakeBroadcastDispatcher,
             )
         val remoteUserSelectionManager =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
similarity index 78%
rename from packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
index 2b8a644..9dc930b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
@@ -27,18 +27,22 @@
 import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
 import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.data.repository.setSceneTransition
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
 import junit.framework.Assert.assertEquals
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.flowOf
@@ -192,6 +196,175 @@
         }
 
     @Test
+    @EnableSceneContainer
+    fun surfaceBehindVisibility_fromLockscreenToGone_trueThroughout() =
+        testScope.runTest {
+            val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
+            val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+
+            // Before the transition, we start on Lockscreen so the surface should start invisible.
+            kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Lockscreen))
+            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+            assertThat(isSurfaceBehindVisible).isFalse()
+
+            // Unlocked with fingerprint.
+            kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+                SuccessFingerprintAuthenticationStatus(0, true)
+            )
+
+            // Start the transition to Gone, the surface should become immediately visible.
+            kosmos.setSceneTransition(
+                ObservableTransitionState.Transition(
+                    fromScene = Scenes.Lockscreen,
+                    toScene = Scenes.Gone,
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
+                    progress = flowOf(0.3f),
+                    currentScene = flowOf(Scenes.Lockscreen),
+                )
+            )
+            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+            assertThat(isSurfaceBehindVisible).isTrue()
+
+            // Towards the end of the transition, the surface should continue to be visible.
+            kosmos.setSceneTransition(
+                ObservableTransitionState.Transition(
+                    fromScene = Scenes.Lockscreen,
+                    toScene = Scenes.Gone,
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
+                    progress = flowOf(0.9f),
+                    currentScene = flowOf(Scenes.Gone),
+                )
+            )
+            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+            assertThat(isSurfaceBehindVisible).isTrue()
+
+            // After the transition, settles on Gone. Surface behind should stay visible now.
+            kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Gone))
+            kosmos.sceneInteractor.changeScene(Scenes.Gone, "")
+            assertThat(currentScene).isEqualTo(Scenes.Gone)
+            assertThat(isSurfaceBehindVisible).isTrue()
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun surfaceBehindVisibility_fromBouncerToGone_becomesTrue() =
+        testScope.runTest {
+            val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
+            val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+
+            // Before the transition, we start on Bouncer so the surface should start invisible.
+            kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Bouncer))
+            kosmos.sceneInteractor.changeScene(Scenes.Bouncer, "")
+            assertThat(currentScene).isEqualTo(Scenes.Bouncer)
+            assertThat(isSurfaceBehindVisible).isFalse()
+
+            // Unlocked with fingerprint.
+            kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+                SuccessFingerprintAuthenticationStatus(0, true)
+            )
+
+            // Start the transition to Gone, the surface should remain invisible prior to hitting
+            // the
+            // threshold.
+            kosmos.setSceneTransition(
+                ObservableTransitionState.Transition(
+                    fromScene = Scenes.Bouncer,
+                    toScene = Scenes.Gone,
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
+                    progress =
+                        flowOf(
+                            FromPrimaryBouncerTransitionInteractor
+                                .TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD
+                        ),
+                    currentScene = flowOf(Scenes.Bouncer),
+                )
+            )
+            assertThat(currentScene).isEqualTo(Scenes.Bouncer)
+            assertThat(isSurfaceBehindVisible).isFalse()
+
+            // Once the transition passes the threshold, the surface should become visible.
+            kosmos.setSceneTransition(
+                ObservableTransitionState.Transition(
+                    fromScene = Scenes.Bouncer,
+                    toScene = Scenes.Gone,
+                    isInitiatedByUserInput = false,
+                    isUserInputOngoing = flowOf(false),
+                    progress =
+                        flowOf(
+                            FromPrimaryBouncerTransitionInteractor
+                                .TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD + 0.01f
+                        ),
+                    currentScene = flowOf(Scenes.Gone),
+                )
+            )
+            assertThat(currentScene).isEqualTo(Scenes.Bouncer)
+            assertThat(isSurfaceBehindVisible).isTrue()
+
+            // After the transition, settles on Gone. Surface behind should stay visible now.
+            kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Gone))
+            kosmos.sceneInteractor.changeScene(Scenes.Gone, "")
+            assertThat(currentScene).isEqualTo(Scenes.Gone)
+            assertThat(isSurfaceBehindVisible).isTrue()
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun surfaceBehindVisibility_idleWhileUnlocked_alwaysTrue() =
+        testScope.runTest {
+            val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
+            val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+
+            // Unlocked with fingerprint.
+            kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+                SuccessFingerprintAuthenticationStatus(0, true)
+            )
+            kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Gone))
+            kosmos.sceneInteractor.changeScene(Scenes.Gone, "")
+            assertThat(currentScene).isEqualTo(Scenes.Gone)
+
+            listOf(
+                    Scenes.Shade,
+                    Scenes.QuickSettings,
+                    Scenes.Shade,
+                    Scenes.Gone,
+                )
+                .forEach { scene ->
+                    kosmos.setSceneTransition(ObservableTransitionState.Idle(scene))
+                    kosmos.sceneInteractor.changeScene(scene, "")
+                    assertThat(currentScene).isEqualTo(scene)
+                    assertWithMessage("Unexpected visibility for scene \"${scene.debugName}\"")
+                        .that(isSurfaceBehindVisible)
+                        .isTrue()
+                }
+        }
+
+    @Test
+    @EnableSceneContainer
+    fun surfaceBehindVisibility_idleWhileLocked_alwaysFalse() =
+        testScope.runTest {
+            val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility)
+            val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
+            assertThat(currentScene).isEqualTo(Scenes.Lockscreen)
+
+            listOf(
+                    Scenes.Shade,
+                    Scenes.QuickSettings,
+                    Scenes.Shade,
+                    Scenes.Lockscreen,
+                )
+                .forEach { scene ->
+                    kosmos.setSceneTransition(ObservableTransitionState.Idle(scene))
+                    kosmos.sceneInteractor.changeScene(scene, "")
+                    assertWithMessage("Unexpected visibility for scene \"${scene.debugName}\"")
+                        .that(isSurfaceBehindVisible)
+                        .isFalse()
+                }
+        }
+
+    @Test
     @DisableSceneContainer
     fun testUsingGoingAwayAnimation_duringTransitionToGone() =
         testScope.runTest {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
index 04c270d..ad24a71 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModelTest.kt
@@ -28,6 +28,7 @@
 import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.BurnInModel
 import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
 import com.android.systemui.kosmos.testScope
@@ -104,6 +105,7 @@
                 burnInInteractor = burnInInteractor,
                 shortcutsCombinedViewModel = shortcutsCombinedViewModel,
                 configurationInteractor = ConfigurationInteractor(FakeConfigurationRepository()),
+                keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor,
             )
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
index 37d4721..7ebebd7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/TileSpecSettingsRepositoryTest.kt
@@ -203,6 +203,21 @@
                 .containsExactlyElementsIn(DEFAULT_TILES.toTileSpecs() + startingTiles)
         }
 
+    @Test
+    fun prependDefault_noChangesWhenInRetail() =
+        testScope.runTest {
+            val user = 0
+            retailModeRepository.setRetailMode(true)
+            val startingTiles = "a"
+            storeTilesForUser(startingTiles, user)
+
+            runCurrent()
+            underTest.prependDefault(user)
+            runCurrent()
+
+            assertThat(loadTilesForUser(user)).isEqualTo(startingTiles)
+        }
+
     private fun TestScope.storeTilesForUser(specs: String, forUser: Int) {
         secureSettings.putStringForUser(SETTING, specs, forUser)
         runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
index 58fc109..b12fbc2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepositoryTest.kt
@@ -327,6 +327,32 @@
             assertThat(loadTiles()).isEqualTo(expected)
         }
 
+    @Test
+    fun setTilesWithRepeats_onlyDistinctTiles() =
+        testScope.runTest {
+            val tilesToSet = "a,b,c,a,d,b".toTileSpecs()
+            val expected = "a,b,c,d"
+
+            val tiles by collectLastValue(underTest.tiles())
+            underTest.setTiles(tilesToSet)
+
+            assertThat(tiles).isEqualTo(expected.toTileSpecs())
+            assertThat(loadTiles()).isEqualTo(expected)
+        }
+
+    @Test
+    fun prependDefaultTwice_doesntAddMoreTiles() =
+        testScope.runTest {
+            val tiles by collectLastValue(underTest.tiles())
+            underTest.setTiles(listOf(TileSpec.create("a")))
+
+            underTest.prependDefault()
+            val currentTiles = tiles!!
+            underTest.prependDefault()
+
+            assertThat(tiles).isEqualTo(currentTiles)
+        }
+
     private fun getDefaultTileSpecs(): List<TileSpec> {
         return defaultTilesRepository.defaultTiles
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableListTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableListTest.kt
index 311122d..16f30fe 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableListTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableListTest.kt
@@ -27,6 +27,7 @@
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.tiles.ColorCorrectionTile
 import com.android.systemui.qs.tiles.ColorInversionTile
+import com.android.systemui.qs.tiles.HearingDevicesTile
 import com.android.systemui.qs.tiles.OneHandedModeTile
 import com.android.systemui.qs.tiles.ReduceBrightColorsTile
 import com.android.systemui.util.mockito.mock
@@ -77,6 +78,10 @@
                     TileSpec.create(ReduceBrightColorsTile.TILE_SPEC),
                     AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME
                 ),
+                factory.create(
+                    TileSpec.create(HearingDevicesTile.TILE_SPEC),
+                    AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME
+                ),
             )
 
         val autoAddables = A11yShortcutAutoAddableList.getA11yShortcutAutoAddables(factory)
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 1c73fe2b..6ad4b31 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,6 +49,7 @@
 import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
 import com.android.systemui.qs.tiles.di.NewQSTileFactory
 import com.android.systemui.qs.toProto
+import com.android.systemui.retail.data.repository.FakeRetailModeRepository
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.user.data.repository.FakeUserRepository
 import com.android.systemui.util.mockito.any
@@ -85,6 +86,7 @@
     private val pipelineFlags = QSPipelineFlagsRepository()
     private val tileLifecycleManagerFactory = TLMFactory()
     private val minimumTilesRepository = MinimumTilesFixedRepository()
+    private val retailModeRepository = FakeRetailModeRepository()
 
     @Mock private lateinit var customTileStatePersister: CustomTileStatePersister
 
@@ -118,6 +120,7 @@
                 installedTilesComponentRepository = installedTilesPackageRepository,
                 userRepository = userRepository,
                 minimumTilesRepository = minimumTilesRepository,
+                retailModeRepository = retailModeRepository,
                 customTileStatePersister = customTileStatePersister,
                 tileFactory = tileFactory,
                 newQSTileFactory = { newQSTileFactory },
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 260189d..e8ad038 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
@@ -34,6 +34,8 @@
 import com.android.systemui.qs.pipeline.data.repository.fakeDefaultTilesRepository
 import com.android.systemui.qs.pipeline.data.repository.fakeMinimumTilesRepository
 import com.android.systemui.qs.pipeline.data.repository.fakeRestoreRepository
+import com.android.systemui.qs.pipeline.data.repository.fakeRetailModeRepository
+import com.android.systemui.qs.pipeline.data.repository.fakeTileSpecRepository
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.qsTileFactory
 import com.android.systemui.settings.fakeUserTracker
@@ -138,6 +140,19 @@
             }
         }
 
+    @Test
+    fun inRetailMode_onlyOneTile_noPrependDefault() =
+        with(kosmos) {
+            testScope.runTest {
+                fakeRetailModeRepository.setRetailMode(true)
+                fakeTileSpecRepository.setTiles(0, listOf(goodTile))
+                val tiles by collectLastValue(currentTilesInteractor.currentTiles)
+                runCurrent()
+
+                assertThat(tiles!!.map { it.spec }).isEqualTo(listOf(goodTile))
+            }
+        }
+
     private fun tileCreator(spec: String): QSTile? {
         return if (spec.contains("OEM")) {
             null // We don't know how to create OEM spec tiles
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 afe7b8f..7388d51 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
@@ -54,9 +54,11 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyFloat
 import org.mockito.Mockito.anyInt
 import org.mockito.Mockito.clearInvocations
 import org.mockito.Mockito.inOrder
+import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 
 @SmallTest
@@ -162,16 +164,25 @@
 
             with(qsImpl!!) {
                 verify(this).setQsVisible(false)
-                verify(this)
+                verify(this, never())
                     .setQsExpansion(
-                        /* expansion= */ 0f,
-                        /* panelExpansionFraction= */ 1f,
-                        /* proposedTranslation= */ 0f,
-                        /* squishinessFraction= */ 1f,
+                        /* expansion= */ anyFloat(),
+                        /* panelExpansionFraction= */ anyFloat(),
+                        /* proposedTranslation= */ anyFloat(),
+                        /* squishinessFraction= */ anyFloat(),
                     )
                 verify(this).setListening(false)
                 verify(this).setExpanded(false)
             }
+
+            underTest.applyLatestExpansionAndSquishiness()
+            verify(qsImpl!!)
+                .setQsExpansion(
+                    /* expansion= */ 0f,
+                    /* panelExpansionFraction= */ 1f,
+                    /* proposedTranslation= */ 0f,
+                    /* squishinessFraction= */ 1f,
+                )
         }
 
     @Test
@@ -186,16 +197,25 @@
             underTest.setState(QSSceneAdapter.State.QQS)
             with(qsImpl!!) {
                 verify(this).setQsVisible(true)
-                verify(this)
+                verify(this, never())
                     .setQsExpansion(
-                        /* expansion= */ 0f,
-                        /* panelExpansionFraction= */ 1f,
-                        /* proposedTranslation= */ 0f,
-                        /* squishinessFraction= */ 1f,
+                        /* expansion= */ anyFloat(),
+                        /* panelExpansionFraction= */ anyFloat(),
+                        /* proposedTranslation= */ anyFloat(),
+                        /* squishinessFraction= */ anyFloat(),
                     )
                 verify(this).setListening(true)
                 verify(this).setExpanded(false)
             }
+
+            underTest.applyLatestExpansionAndSquishiness()
+            verify(qsImpl!!)
+                .setQsExpansion(
+                    /* expansion= */ 0f,
+                    /* panelExpansionFraction= */ 1f,
+                    /* proposedTranslation= */ 0f,
+                    /* squishinessFraction= */ 1f,
+                )
         }
 
     @Test
@@ -210,16 +230,25 @@
             underTest.setState(QSSceneAdapter.State.QS)
             with(qsImpl!!) {
                 verify(this).setQsVisible(true)
-                verify(this)
+                verify(this, never())
                     .setQsExpansion(
-                        /* expansion= */ 1f,
-                        /* panelExpansionFraction= */ 1f,
-                        /* proposedTranslation= */ 0f,
-                        /* squishinessFraction= */ 1f,
+                        /* expansion= */ anyFloat(),
+                        /* panelExpansionFraction= */ anyFloat(),
+                        /* proposedTranslation= */ anyFloat(),
+                        /* squishinessFraction= */ anyFloat(),
                     )
                 verify(this).setListening(true)
                 verify(this).setExpanded(true)
             }
+
+            underTest.applyLatestExpansionAndSquishiness()
+            verify(qsImpl!!)
+                .setQsExpansion(
+                    /* expansion= */ 1f,
+                    /* panelExpansionFraction= */ 1f,
+                    /* proposedTranslation= */ 0f,
+                    /* squishinessFraction= */ 1f,
+                )
         }
 
     @Test
@@ -235,16 +264,25 @@
             underTest.setState(QSSceneAdapter.State.Expanding(progress))
             with(qsImpl!!) {
                 verify(this).setQsVisible(true)
-                verify(this)
+                verify(this, never())
                     .setQsExpansion(
-                        /* expansion= */ progress,
-                        /* panelExpansionFraction= */ 1f,
-                        /* proposedTranslation= */ 0f,
-                        /* squishinessFraction= */ 1f,
+                        /* expansion= */ anyFloat(),
+                        /* panelExpansionFraction= */ anyFloat(),
+                        /* proposedTranslation= */ anyFloat(),
+                        /* squishinessFraction= */ anyFloat(),
                     )
                 verify(this).setListening(true)
                 verify(this).setExpanded(true)
             }
+
+            underTest.applyLatestExpansionAndSquishiness()
+            verify(qsImpl!!)
+                .setQsExpansion(
+                    /* expansion= */ progress,
+                    /* panelExpansionFraction= */ 1f,
+                    /* proposedTranslation= */ 0f,
+                    /* squishinessFraction= */ 1f,
+                )
         }
 
     @Test
@@ -260,16 +298,25 @@
             underTest.setState(QSSceneAdapter.State.UnsquishingQQS { squishiness })
             with(qsImpl!!) {
                 verify(this).setQsVisible(true)
-                verify(this)
+                verify(this, never())
                     .setQsExpansion(
-                        /* expansion= */ 0f,
-                        /* panelExpansionFraction= */ 1f,
-                        /* proposedTranslation= */ 0f,
-                        /* squishinessFraction= */ squishiness,
+                        /* expansion= */ anyFloat(),
+                        /* panelExpansionFraction= */ anyFloat(),
+                        /* proposedTranslation= */ anyFloat(),
+                        /* squishinessFraction= */ anyFloat(),
                     )
                 verify(this).setListening(true)
                 verify(this).setExpanded(false)
             }
+
+            underTest.applyLatestExpansionAndSquishiness()
+            verify(qsImpl!!)
+                .setQsExpansion(
+                    /* expansion= */ 0f,
+                    /* panelExpansionFraction= */ 1f,
+                    /* proposedTranslation= */ 0f,
+                    /* squishinessFraction= */ squishiness,
+                )
         }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/ScrimStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/ScrimStartableTest.kt
new file mode 100644
index 0000000..0e90afe
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/ScrimStartableTest.kt
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.scene.domain.startable
+
+import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.biometricUnlockInteractor
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.DozeTransitionModel
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.setSceneTransition
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.settings.brightness.domain.interactor.brightnessMirrorShowingInteractor
+import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor
+import com.android.systemui.statusbar.phone.BiometricUnlockController
+import com.android.systemui.statusbar.phone.ScrimState
+import com.android.systemui.statusbar.phone.centralSurfaces
+import com.android.systemui.statusbar.phone.dozeServiceHost
+import com.android.systemui.statusbar.phone.statusBarKeyguardViewManager
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import com.google.common.truth.Truth.assertWithMessage
+import kotlin.reflect.full.memberProperties
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.BeforeClass
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+import platform.test.runner.parameterized.Parameter
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+
+@SmallTest
+@RunWith(ParameterizedAndroidJunit4::class)
+@EnableSceneContainer
+class ScrimStartableTest : SysuiTestCase() {
+
+    companion object {
+        @Parameters(name = "{0}")
+        @JvmStatic
+        fun testSpecs(): List<TestSpec> {
+            return listOf(
+                TestSpec(
+                    id = 0,
+                    expectedState = ScrimState.KEYGUARD,
+                    Preconditions(
+                        isOnKeyguard = true,
+                        isAlternateBouncerVisible = true,
+                        isTransitioningAwayFromKeyguard = true,
+                    ),
+                ),
+                TestSpec(
+                    id = 1,
+                    expectedState = null,
+                    Preconditions(
+                        isOnKeyguard = true,
+                        isAlternateBouncerVisible = true,
+                        isTransitioningToShade = true,
+                    ),
+                ),
+                TestSpec(
+                    id = 2,
+                    expectedState = ScrimState.BOUNCER,
+                    Preconditions(
+                        isOnKeyguard = true,
+                        isCurrentSceneBouncer = true,
+                    ),
+                ),
+                TestSpec(
+                    id = 3,
+                    expectedState = ScrimState.BOUNCER_SCRIMMED,
+                    Preconditions(
+                        isOnKeyguard = true,
+                        isCurrentSceneBouncer = true,
+                        isBouncerScrimmingNeeded = true,
+                    ),
+                ),
+                TestSpec(
+                    id = 4,
+                    expectedState = ScrimState.BRIGHTNESS_MIRROR,
+                    Preconditions(
+                        isOnKeyguard = true,
+                        isBrightnessMirrorVisible = true,
+                    ),
+                ),
+                TestSpec(
+                    id = 5,
+                    expectedState = ScrimState.BRIGHTNESS_MIRROR,
+                    Preconditions(
+                        isOnKeyguard = true,
+                        isCurrentSceneBouncer = true,
+                        isBiometricWakeAndUnlock = true,
+                        isBrightnessMirrorVisible = true,
+                    ),
+                ),
+                TestSpec(
+                    id = 6,
+                    expectedState = ScrimState.SHADE_LOCKED,
+                    Preconditions(
+                        isOnKeyguard = true,
+                        isCurrentSceneShade = true,
+                    ),
+                ),
+                TestSpec(
+                    id = 7,
+                    expectedState = ScrimState.PULSING,
+                    Preconditions(
+                        isOnKeyguard = true,
+                        isDozing = true,
+                        isPulsing = true,
+                    ),
+                ),
+                TestSpec(
+                    id = 8,
+                    expectedState = ScrimState.OFF,
+                    Preconditions(
+                        isOnKeyguard = true,
+                        hasPendingScreenOffCallback = true,
+                    ),
+                ),
+                TestSpec(
+                    id = 9,
+                    expectedState = ScrimState.AOD,
+                    Preconditions(
+                        isOnKeyguard = true,
+                        isDozing = true,
+                    ),
+                ),
+                TestSpec(
+                    id = 10,
+                    expectedState = ScrimState.GLANCEABLE_HUB,
+                    Preconditions(
+                        isIdleOnCommunal = true,
+                    ),
+                ),
+                TestSpec(
+                    id = 11,
+                    expectedState = ScrimState.GLANCEABLE_HUB_OVER_DREAM,
+                    Preconditions(isIdleOnCommunal = true, isDreaming = true),
+                ),
+                TestSpec(
+                    id = 12,
+                    expectedState = ScrimState.UNLOCKED,
+                    Preconditions(
+                        isDeviceEntered = true,
+                    ),
+                ),
+                TestSpec(
+                    id = 13,
+                    expectedState = ScrimState.UNLOCKED,
+                    Preconditions(
+                        isOnKeyguard = true,
+                        isBiometricWakeAndUnlock = true,
+                    ),
+                ),
+                TestSpec(
+                    id = 14,
+                    expectedState = ScrimState.KEYGUARD,
+                    Preconditions(),
+                ),
+                TestSpec(
+                    id = 15,
+                    expectedState = ScrimState.DREAMING,
+                    Preconditions(
+                        isOnKeyguard = true,
+                        isOccluded = true,
+                        isDreaming = true,
+                    ),
+                ),
+                TestSpec(
+                    id = 16,
+                    expectedState = ScrimState.UNLOCKED,
+                    Preconditions(
+                        isOnKeyguard = true,
+                        isOccluded = true,
+                    ),
+                ),
+            )
+        }
+
+        @BeforeClass
+        @JvmStatic
+        fun setUpClass() {
+            val seenIds = mutableSetOf<Int>()
+            testSpecs().forEach { testSpec ->
+                assertWithMessage("Duplicate TestSpec id=${testSpec.id}")
+                    .that(seenIds)
+                    .doesNotContain(testSpec.id)
+                seenIds.add(testSpec.id)
+            }
+        }
+    }
+
+    private val kosmos = testKosmos()
+    private val testScope = kosmos.testScope
+
+    private val underTest = kosmos.scrimStartable
+
+    @JvmField @Parameter(0) var testSpec: TestSpec? = null
+
+    @Before
+    fun setUp() {
+        kosmos.dozeServiceHost.initialize(
+            /* centralSurfaces= */ kosmos.centralSurfaces,
+            /* statusBarKeyguardViewManager= */ kosmos.statusBarKeyguardViewManager,
+            /* notificationShadeWindowViewController= */ mock(),
+            /* ambientIndicationContainer= */ mock(),
+        )
+        underTest.start()
+    }
+
+    @Test
+    fun test() =
+        testScope.runTest {
+            val observedState by collectLastValue(underTest.scrimState)
+            val preconditions = checkNotNull(testSpec).preconditions
+            preconditions.assertValid()
+
+            setUpWith(preconditions)
+
+            runCurrent()
+
+            assertThat(observedState).isEqualTo(checkNotNull(testSpec).expectedState)
+        }
+
+    /** Sets up the state to match what's specified in the given [preconditions]. */
+    private fun TestScope.setUpWith(
+        preconditions: Preconditions,
+    ) {
+        kosmos.fakeKeyguardBouncerRepository.setAlternateVisible(
+            preconditions.isAlternateBouncerVisible
+        )
+
+        if (preconditions.isDeviceEntered) {
+            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+                SuccessFingerprintAuthenticationStatus(0, true)
+            )
+            whenIdle(on = Scenes.Gone)
+        } else {
+            whenIdle(on = Scenes.Lockscreen)
+        }
+        runCurrent()
+
+        when {
+            preconditions.isTransitioningToShade ->
+                whenTransitioning(
+                    from = Scenes.Lockscreen,
+                    to = Scenes.Shade,
+                )
+            preconditions.isTransitioningAwayFromKeyguard ->
+                whenTransitioning(
+                    from = Scenes.Lockscreen,
+                    to = Scenes.Gone,
+                )
+            preconditions.isCurrentSceneShade -> whenIdle(on = Scenes.Shade)
+            preconditions.isCurrentSceneBouncer -> whenIdle(on = Scenes.Bouncer)
+            preconditions.isIdleOnCommunal -> whenIdle(on = Scenes.Communal)
+        }
+
+        kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(
+            showWhenLockedActivityOnTop = preconditions.isOccluded,
+            taskInfo = if (preconditions.isOccluded) mock() else null,
+        )
+
+        if (preconditions.isBiometricWakeAndUnlock) {
+            kosmos.biometricUnlockInteractor.setBiometricUnlockState(
+                BiometricUnlockController.MODE_WAKE_AND_UNLOCK,
+                BiometricUnlockSource.FINGERPRINT_SENSOR,
+            )
+        }
+
+        kosmos.brightnessMirrorShowingInteractor.setMirrorShowing(
+            preconditions.isBrightnessMirrorVisible
+        )
+
+        if (preconditions.hasPendingScreenOffCallback) {
+            kosmos.dozeServiceHost.prepareForGentleSleep {}
+        } else {
+            kosmos.dozeServiceHost.cancelGentleSleep()
+        }
+
+        kosmos.fakeKeyguardRepository.setIsDozing(preconditions.isDozing)
+        if (preconditions.isPulsing) {
+            kosmos.fakeKeyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(to = DozeStateModel.DOZE_PULSING)
+            )
+        }
+        kosmos.fakeKeyguardRepository.setDreaming(preconditions.isDreaming)
+
+        whenever(kosmos.statusBarKeyguardViewManager.primaryBouncerNeedsScrimming())
+            .thenReturn(preconditions.isBouncerScrimmingNeeded)
+
+        runCurrent()
+    }
+
+    /** Sets up an idle state on the given [on] scene. */
+    private fun whenIdle(on: SceneKey) {
+        kosmos.setSceneTransition(ObservableTransitionState.Idle(on))
+        kosmos.sceneInteractor.changeScene(on, "")
+    }
+
+    /** Sets up a transitioning state between the [given] and [to] scenes. */
+    private fun whenTransitioning(from: SceneKey, to: SceneKey, progress: Float = 0.5f) {
+        val currentScene = if (progress > 0.5f) to else from
+        kosmos.setSceneTransition(
+            ObservableTransitionState.Transition(
+                fromScene = from,
+                toScene = to,
+                progress = flowOf(progress),
+                currentScene = flowOf(currentScene),
+                isInitiatedByUserInput = true,
+                isUserInputOngoing = flowOf(false),
+            )
+        )
+        kosmos.sceneInteractor.changeScene(currentScene, "")
+    }
+
+    data class Preconditions(
+        /** Whether bouncer or lockscreen scene is in the nav stack. */
+        val isOnKeyguard: Boolean = false,
+        val isAlternateBouncerVisible: Boolean = false,
+        /** Whether any non-shade nor QS scene is transitioning to a shade or QS scene. */
+        val isTransitioningToShade: Boolean = false,
+        val isOccluded: Boolean = false,
+        val isCurrentSceneBouncer: Boolean = false,
+        val isBiometricWakeAndUnlock: Boolean = false,
+        /** Whether there's an active transition from lockscreen or bouncer to gone. */
+        val isTransitioningAwayFromKeyguard: Boolean = false,
+        val isBrightnessMirrorVisible: Boolean = false,
+        /** Whether the current scene is a shade or QS scene. */
+        val isCurrentSceneShade: Boolean = false,
+        val isDeviceEntered: Boolean = false,
+        val isPulsing: Boolean = false,
+        val hasPendingScreenOffCallback: Boolean = false,
+        val isDozing: Boolean = false,
+        val isIdleOnCommunal: Boolean = false,
+        val isDreaming: Boolean = false,
+        val isBouncerScrimmingNeeded: Boolean = false,
+    ) {
+        override fun toString(): String {
+            // Only include values overridden to true:
+            return buildString {
+                append("(")
+                append(
+                    Preconditions::class
+                        .memberProperties
+                        .filter { it.get(this@Preconditions) == true }
+                        .joinToString(", ") { "${it.name}=true" }
+                )
+                append(")")
+            }
+        }
+
+        fun assertValid() {
+            assertWithMessage("isOccluded cannot be true without isOnKeyguard also being true")
+                .that(!isOccluded || isOnKeyguard)
+                .isTrue()
+
+            assertWithMessage(
+                    "isCurrentSceneBouncer cannot be true without isOnKeyguard also being true"
+                )
+                .that(!isCurrentSceneBouncer || isOnKeyguard)
+                .isTrue()
+
+            assertWithMessage(
+                    "isTransitioningAwayFromKeyguard cannot be true without isOnKeyguard being true"
+                )
+                .that(!isTransitioningAwayFromKeyguard || isOnKeyguard)
+                .isTrue()
+
+            assertWithMessage(
+                    "isCurrentSceneBouncer cannot be true at the same time as isCurrentSceneShade"
+                )
+                .that(!isCurrentSceneBouncer || !isCurrentSceneShade)
+                .isTrue()
+
+            assertWithMessage(
+                    "isCurrentSceneBouncer cannot be true at the same time as isIdleOnCommunal"
+                )
+                .that(!isCurrentSceneBouncer || !isIdleOnCommunal)
+                .isTrue()
+
+            assertWithMessage(
+                    "isCurrentSceneShade cannot be true at the same time as isIdleOnCommunal"
+                )
+                .that(!isCurrentSceneShade || !isIdleOnCommunal)
+                .isTrue()
+
+            assertWithMessage("isDeviceEntered cannot be true at the same time as isOnKeyguard")
+                .that(!isDeviceEntered || !isOnKeyguard)
+                .isTrue()
+
+            assertWithMessage(
+                    "isDeviceEntered cannot be true at the same time as isCurrentSceneBouncer"
+                )
+                .that(!isDeviceEntered || !isCurrentSceneBouncer)
+                .isTrue()
+
+            assertWithMessage(
+                    "isDeviceEntered cannot be true at the same time as isAlternateBouncerVisible"
+                )
+                .that(!isDeviceEntered || !isAlternateBouncerVisible)
+                .isTrue()
+
+            assertWithMessage("isPulsing cannot be true if both isDozing is false")
+                .that(!isPulsing || isDozing)
+                .isTrue()
+        }
+    }
+
+    data class TestSpec(
+        val id: Int,
+        val expectedState: ScrimState?,
+        val preconditions: Preconditions,
+    ) {
+        override fun toString(): String {
+            return "id=$id, expected=$expectedState, preconditions=$preconditions"
+        }
+    }
+}
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 02993b8..523a89a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -99,14 +99,14 @@
 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.Collection;
 import java.util.List;
 import java.util.concurrent.Executor;
 
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4.class)
 public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
@@ -162,6 +162,7 @@
     private NotificationEntry mCurrentUserNotif;
     private NotificationEntry mSecondaryUserNotif;
     private NotificationEntry mWorkProfileNotif;
+    private NotificationEntry mSensitiveContentNotif;
     private final FakeFeatureFlagsClassic mFakeFeatureFlags = new FakeFeatureFlagsClassic();
     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
     private final FakeExecutor mBackgroundExecutor = new FakeExecutor(mFakeSystemClock);
@@ -224,6 +225,14 @@
         mWorkProfileNotif.setRanking(new RankingBuilder(mWorkProfileNotif.getRanking())
                 .setChannel(channel)
                 .setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build());
+        mSensitiveContentNotif = new NotificationEntryBuilder()
+                .setNotification(notifWithPrivateVisibility)
+                .setUser(new UserHandle(mCurrentUser.id))
+                .build();
+        mSensitiveContentNotif.setRanking(new RankingBuilder(mCurrentUserNotif.getRanking())
+                .setChannel(channel)
+                .setSensitiveContent(true)
+                .setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build());
         when(mNotifCollection.getEntry(mWorkProfileNotif.getKey())).thenReturn(mWorkProfileNotif);
 
         mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext);
@@ -459,6 +468,17 @@
     }
 
     @Test
+    public void testHasSensitiveContent_redacted() {
+        // Allow private notifications for this user
+        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
+                mCurrentUser.id);
+        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+
+        // Sensitive Content notifications are always redacted
+        assertTrue(mLockscreenUserManager.needsRedaction(mSensitiveContentNotif));
+    }
+
+    @Test
     public void testUserSwitchedCallsOnUserSwitching() {
         mLockscreenUserManager.getUserTrackerCallbackForTest().onUserChanging(mSecondaryUser.id,
                 mContext);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
index 8cb811d..fed6131 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationMediaManagerTest.kt
@@ -16,6 +16,10 @@
 
 package com.android.systemui.statusbar
 
+import android.media.MediaMetadata
+import android.media.session.MediaController
+import android.media.session.MediaSession
+import android.os.fakeExecutorHandler
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.service.notification.NotificationListenerService
@@ -54,6 +58,7 @@
     private val notifPipeline = kosmos.notifPipeline
     private val notifCollection = kosmos.mockNotifCollection
     private val dumpManager = kosmos.dumpManager
+    private val handler = kosmos.fakeExecutorHandler
     private val mediaDataManager = mock<MediaDataManager>()
     private val backgroundExecutor = FakeExecutor(FakeSystemClock())
 
@@ -72,13 +77,17 @@
                 mediaDataManager,
                 dumpManager,
                 backgroundExecutor,
+                handler,
             )
+        val mediaSession = MediaSession(context, "TEST")
+        notificationMediaManager.mMediaController =
+            MediaController(context, mediaSession.sessionToken)
 
         verify(mediaDataManager).addListener(listenerCaptor.capture())
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DISMISS)
+    @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DELETEINTENT)
     fun mediaDataRemoved_userInitiated_dismissNotif() {
         val notifEntryCaptor = argumentCaptor<NotificationEntry>()
         val notifEntry = mock<NotificationEntry>()
@@ -93,7 +102,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DISMISS)
+    @EnableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DELETEINTENT)
     fun mediaDataRemoved_notUserInitiated_doesNotDismissNotif() {
         listenerCaptor.lastValue.onMediaDataRemoved(KEY, false)
 
@@ -101,7 +110,7 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DISMISS)
+    @DisableFlags(Flags.FLAG_MEDIA_CONTROLS_USER_INITIATED_DELETEINTENT)
     fun mediaDataRemoved_notUserInitiated_flagOff_dismissNotif() {
         val notifEntryCaptor = argumentCaptor<NotificationEntry>()
         val notifEntry = mock<NotificationEntry>()
@@ -114,4 +123,32 @@
         verify(notifCollection).dismissNotification(notifEntryCaptor.capture(), any())
         assertThat(notifEntryCaptor.lastValue.key).isEqualTo(KEY)
     }
+
+    @Test
+    @EnableFlags(Flags.FLAG_NOTIFICATION_MEDIA_MANAGER_BACKGROUND_EXECUTION)
+    fun clearMediaNotification_flagOn_resetMediaMetadata() {
+        // set up media metadata.
+        notificationMediaManager.mMediaListener.onMetadataChanged(MediaMetadata.Builder().build())
+        backgroundExecutor.runAllReady()
+
+        // clear media notification.
+        notificationMediaManager.clearCurrentMediaNotification()
+        backgroundExecutor.runAllReady()
+
+        assertThat(notificationMediaManager.mediaMetadata).isNull()
+        assertThat(notificationMediaManager.mMediaController).isNull()
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_NOTIFICATION_MEDIA_MANAGER_BACKGROUND_EXECUTION)
+    fun clearMediaNotification_flagOff_resetMediaMetadata() {
+        // set up media metadata.
+        notificationMediaManager.mMediaListener.onMetadataChanged(MediaMetadata.Builder().build())
+
+        // clear media notification.
+        notificationMediaManager.clearCurrentMediaNotification()
+
+        assertThat(notificationMediaManager.mediaMetadata).isNull()
+        assertThat(notificationMediaManager.mMediaController).isNull()
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 82e2bb7..c35c165 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -41,6 +41,8 @@
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.shared.model.BurnInModel
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.shared.model.StatusBarState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -53,6 +55,7 @@
 import com.android.systemui.res.R
 import com.android.systemui.shade.mockLargeScreenHeaderHelper
 import com.android.systemui.shade.shadeTestUtil
+import com.android.systemui.statusbar.notification.NotificationUtils.interpolate
 import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
@@ -794,11 +797,47 @@
     @DisableSceneContainer
     fun updateBounds_fromKeyguardRoot() =
         testScope.runTest {
-            val bounds by collectLastValue(underTest.bounds)
+            val startProgress = 0f
+            val startStep = TransitionStep(LOCKSCREEN, AOD, startProgress, TransitionState.STARTED)
+            val boundsChangingProgress = 0.2f
+            val boundsChangingStep =
+                TransitionStep(LOCKSCREEN, AOD, boundsChangingProgress, TransitionState.RUNNING)
+            val boundsInterpolatingProgress = 0.6f
+            val boundsInterpolatingStep =
+                TransitionStep(
+                    LOCKSCREEN,
+                    AOD,
+                    boundsInterpolatingProgress,
+                    TransitionState.RUNNING
+                )
+            val finishProgress = 1.0f
+            val finishStep =
+                TransitionStep(LOCKSCREEN, AOD, finishProgress, TransitionState.FINISHED)
 
+            val bounds by collectLastValue(underTest.bounds)
             val top = 123f
             val bottom = 456f
+
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(startStep)
+            runCurrent()
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(boundsChangingStep)
+            runCurrent()
             keyguardRootViewModel.onNotificationContainerBoundsChanged(top, bottom)
+
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(boundsInterpolatingStep)
+            runCurrent()
+            val adjustedProgress =
+                (boundsInterpolatingProgress - boundsChangingProgress) /
+                    (1 - boundsChangingProgress)
+            val interpolatedTop = interpolate(0f, top, adjustedProgress)
+            val interpolatedBottom = interpolate(0f, bottom, adjustedProgress)
+            assertThat(bounds)
+                .isEqualTo(
+                    NotificationContainerBounds(top = interpolatedTop, bottom = interpolatedBottom)
+                )
+
+            kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(finishStep)
+            runCurrent()
             assertThat(bounds).isEqualTo(NotificationContainerBounds(top = top, bottom = bottom))
         }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
index 0ca6207..57d3251 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImplTest.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.animation.LaunchableView
 import com.android.systemui.assist.AssistManager
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.keyguard.KeyguardViewMediator
 import com.android.systemui.keyguard.WakefulnessLifecycle
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction
@@ -94,6 +95,7 @@
     @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
     @Mock private lateinit var userTracker: UserTracker
     @Mock private lateinit var activityIntentHelper: ActivityIntentHelper
+    @Mock private lateinit var communalSceneInteractor: CommunalSceneInteractor
     private lateinit var underTest: LegacyActivityStarterInternalImpl
     private val mainExecutor = FakeExecutor(FakeSystemClock())
     private val shadeAnimationInteractor =
@@ -127,6 +129,7 @@
                 userTracker = userTracker,
                 activityIntentHelper = activityIntentHelper,
                 mainExecutor = mainExecutor,
+                communalSceneInteractor = communalSceneInteractor,
             )
         whenever(userTracker.userHandle).thenReturn(UserHandle.OWNER)
     }
@@ -138,7 +141,7 @@
         whenever(keyguardStateController.isShowing).thenReturn(true)
         whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(true)
 
-        underTest.startPendingIntentDismissingKeyguard(pendingIntent)
+        underTest.startPendingIntentDismissingKeyguard(intent = pendingIntent, dismissShade = true)
         mainExecutor.runAllReady()
 
         verify(statusBarKeyguardViewManager)
@@ -232,6 +235,7 @@
 
         underTest.startPendingIntentDismissingKeyguard(
             intent = pendingIntent,
+            dismissShade = true,
             intentSentUiThreadCallback = null,
             associatedView = associatedView,
         )
@@ -344,6 +348,7 @@
     ) {
         underTest.startPendingIntentDismissingKeyguard(
             intent = intent,
+            dismissShade = true,
             intentSentUiThreadCallback = intentSentUiThreadCallback,
             animationController = animationController,
             showOverLockscreen = true,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
index 63f19fb..6b5d072 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
@@ -78,7 +78,7 @@
 
         // Initialize AvalancheController and TestableHeadsUpManager during setUp instead of
         // declaration, where mocks are null
-        mAvalancheController = AvalancheController(dumpManager)
+        mAvalancheController = AvalancheController(dumpManager, mUiEventLoggerFake)
 
         testableHeadsUpManager =
             TestableHeadsUpManager(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 0f66a93..88bef91 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -38,6 +38,8 @@
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.Person;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.FlagsParameterization;
 import android.testing.TestableLooper;
 
@@ -146,7 +148,63 @@
     @Override
     public void SysuiSetup() throws Exception {
         super.SysuiSetup();
-        mAvalancheController = new AvalancheController(dumpManager);
+        mAvalancheController = new AvalancheController(dumpManager, mUiEventLoggerFake);
+    }
+
+    @Test
+    public void testHasNotifications_headsUpManagerMapNotEmpty_true() {
+        final BaseHeadsUpManager bhum = createHeadsUpManager();
+        final NotificationEntry entry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0, mContext);
+        bhum.showNotification(entry);
+
+        assertThat(bhum.mHeadsUpEntryMap).isNotEmpty();
+        assertThat(bhum.hasNotifications()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+    public void testHasNotifications_avalancheMapNotEmpty_true() {
+        final BaseHeadsUpManager bhum = createHeadsUpManager();
+        final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
+                mContext);
+        final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry);
+        mAvalancheController.addToNext(headsUpEntry, () -> {});
+
+        assertThat(mAvalancheController.getWaitingEntryList()).isNotEmpty();
+        assertThat(bhum.hasNotifications()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+    public void testHasNotifications_false() {
+        final BaseHeadsUpManager bhum = createHeadsUpManager();
+        assertThat(bhum.mHeadsUpEntryMap).isEmpty();
+        assertThat(mAvalancheController.getWaitingEntryList()).isEmpty();
+        assertThat(bhum.hasNotifications()).isFalse();
+    }
+
+    @Test
+    @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+    public void testGetHeadsUpEntryList_includesAvalancheEntryList() {
+        final BaseHeadsUpManager bhum = createHeadsUpManager();
+        final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
+                mContext);
+        final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry);
+        mAvalancheController.addToNext(headsUpEntry, () -> {});
+
+        assertThat(bhum.getHeadsUpEntryList()).contains(headsUpEntry);
+    }
+
+    @Test
+    @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+    public void testGetHeadsUpEntry_returnsAvalancheEntry() {
+        final BaseHeadsUpManager bhum = createHeadsUpManager();
+        final NotificationEntry notifEntry = HeadsUpManagerTestUtil.createEntry(/* id = */ 0,
+                mContext);
+        final BaseHeadsUpManager.HeadsUpEntry headsUpEntry = bhum.createHeadsUpEntry(notifEntry);
+        mAvalancheController.addToNext(headsUpEntry, () -> {});
+
+        assertThat(bhum.getHeadsUpEntry(notifEntry.getKey())).isEqualTo(headsUpEntry);
     }
 
     @Test
@@ -553,7 +611,31 @@
     }
 
     @Test
-    public void testPinEntry_logsPeek() {
+    @EnableFlags(NotificationThrottleHun.FLAG_NAME)
+    public void testPinEntry_logsPeek_throttleEnabled() {
+        final BaseHeadsUpManager hum = createHeadsUpManager();
+
+        // Needs full screen intent in order to be pinned
+        final BaseHeadsUpManager.HeadsUpEntry entryToPin = hum.new HeadsUpEntry(
+                HeadsUpManagerTestUtil.createFullScreenIntentEntry(/* id = */ 0, mContext));
+
+        // Note: the standard way to show a notification would be calling showNotification rather
+        // than onAlertEntryAdded. However, in practice showNotification in effect adds
+        // the notification and then updates it; in order to not log twice, the entry needs
+        // to have a functional ExpandableNotificationRow that can keep track of whether it's
+        // pinned or not (via isRowPinned()). That feels like a lot to pull in to test this one bit.
+        hum.onEntryAdded(entryToPin);
+
+        assertEquals(2, mUiEventLoggerFake.numLogs());
+        assertEquals(AvalancheController.ThrottleEvent.SHOWN.getId(),
+                mUiEventLoggerFake.eventId(0));
+        assertEquals(BaseHeadsUpManager.NotificationPeekEvent.NOTIFICATION_PEEK.getId(),
+                mUiEventLoggerFake.eventId(1));
+    }
+
+    @Test
+    @DisableFlags(NotificationThrottleHun.FLAG_NAME)
+    public void testPinEntry_logsPeek_throttleDisabled() {
         final BaseHeadsUpManager hum = createHeadsUpManager();
 
         // Needs full screen intent in order to be pinned
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java
index 9feb914..200e92e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/HeadsUpManagerPhoneTest.java
@@ -167,7 +167,7 @@
         mContext.getOrCreateTestableResources().addOverride(
                 R.integer.ambient_notification_extension_time, 500);
 
-        mAvalancheController = new AvalancheController(dumpManager);
+        mAvalancheController = new AvalancheController(dumpManager, mUiEventLogger);
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
index cec8ccf..10a4eb7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
@@ -18,33 +18,26 @@
 
 import android.bluetooth.BluetoothDevice
 import android.graphics.drawable.TestStubDrawable
-import android.media.AudioDeviceInfo
-import android.media.AudioDevicePort
 import android.media.AudioManager
 import android.testing.TestableLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.settingslib.R
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
-import com.android.settingslib.media.BluetoothMediaDevice
-import com.android.settingslib.media.MediaDevice
-import com.android.settingslib.media.PhoneMediaDevice
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bluetooth.bluetoothAdapter
 import com.android.systemui.bluetooth.cachedBluetoothDeviceManager
 import com.android.systemui.coroutines.collectLastValue
 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
-import com.android.systemui.util.mockito.whenever
+import com.android.systemui.volume.data.repository.TestAudioDevicesFactory
 import com.android.systemui.volume.data.repository.audioRepository
 import com.android.systemui.volume.data.repository.audioSharingRepository
 import com.android.systemui.volume.domain.model.AudioOutputDevice
 import com.android.systemui.volume.localMediaController
 import com.android.systemui.volume.localMediaRepository
 import com.android.systemui.volume.mediaControllerRepository
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.TestMediaDevicesFactory
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
@@ -52,6 +45,12 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+private const val builtInDeviceName = "This phone"
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @RunWith(AndroidJUnit4::class)
@@ -74,6 +73,10 @@
                 addOverride(R.drawable.ic_media_speaker_device, testIcon)
 
                 addOverride(com.android.internal.R.drawable.ic_bt_hearing_aid, testIcon)
+
+                addOverride(R.string.media_transfer_this_device_name_tv, builtInDeviceName)
+                addOverride(R.string.media_transfer_this_device_name_tablet, builtInDeviceName)
+                addOverride(R.string.media_transfer_this_device_name, builtInDeviceName)
             }
         }
     }
@@ -84,7 +87,7 @@
             testScope.runTest {
                 with(audioRepository) {
                     setMode(AudioManager.MODE_IN_CALL)
-                    setCommunicationDevice(builtInDevice)
+                    setCommunicationDevice(TestAudioDevicesFactory.builtInDevice())
                 }
 
                 val device by collectLastValue(underTest.currentAudioDevice)
@@ -93,7 +96,7 @@
 
                 assertThat(device).isInstanceOf(AudioOutputDevice.BuiltIn::class.java)
                 assertThat(device!!.icon).isEqualTo(testIcon)
-                assertThat(device!!.name).isEqualTo("built_in")
+                assertThat(device!!.name).isEqualTo(builtInDeviceName)
             }
         }
     }
@@ -104,7 +107,7 @@
             testScope.runTest {
                 with(audioRepository) {
                     setMode(AudioManager.MODE_IN_CALL)
-                    setCommunicationDevice(wiredDevice)
+                    setCommunicationDevice(TestAudioDevicesFactory.wiredDevice())
                 }
 
                 val device by collectLastValue(underTest.currentAudioDevice)
@@ -122,17 +125,18 @@
     fun inCall_bluetooth_returnsCommunicationDevice() {
         with(kosmos) {
             testScope.runTest {
+                val btDevice = TestAudioDevicesFactory.bluetoothDevice()
                 with(audioRepository) {
                     setMode(AudioManager.MODE_IN_CALL)
                     setCommunicationDevice(btDevice)
                 }
                 val bluetoothDevice: BluetoothDevice = mock {
-                    whenever(address).thenReturn(btDevice.address)
+                    on { address }.thenReturn(btDevice.address)
                 }
                 val cachedBluetoothDevice: CachedBluetoothDevice = mock {
-                    whenever(address).thenReturn(btDevice.address)
-                    whenever(name).thenReturn(btDevice.productName.toString())
-                    whenever(isHearingAidDevice).thenReturn(true)
+                    on { address }.thenReturn(btDevice.address)
+                    on { name }.thenReturn(btDevice.productName.toString())
+                    on { isHearingAidDevice }.thenReturn(true)
                 }
                 whenever(bluetoothAdapter.getRemoteDevice(eq(btDevice.address)))
                     .thenReturn(bluetoothDevice)
@@ -156,7 +160,9 @@
             testScope.runTest {
                 audioRepository.setMode(AudioManager.MODE_NORMAL)
                 mediaControllerRepository.setActiveSessions(listOf(localMediaController))
-                localMediaRepository.updateCurrentConnectedDevice(builtInMediaDevice)
+                localMediaRepository.updateCurrentConnectedDevice(
+                    TestMediaDevicesFactory.builtInMediaDevice()
+                )
 
                 val device by collectLastValue(underTest.currentAudioDevice)
 
@@ -175,7 +181,9 @@
             testScope.runTest {
                 audioRepository.setMode(AudioManager.MODE_NORMAL)
                 mediaControllerRepository.setActiveSessions(listOf(localMediaController))
-                localMediaRepository.updateCurrentConnectedDevice(wiredMediaDevice)
+                localMediaRepository.updateCurrentConnectedDevice(
+                    TestMediaDevicesFactory.wiredMediaDevice()
+                )
 
                 val device by collectLastValue(underTest.currentAudioDevice)
 
@@ -194,7 +202,9 @@
             testScope.runTest {
                 audioRepository.setMode(AudioManager.MODE_NORMAL)
                 mediaControllerRepository.setActiveSessions(listOf(localMediaController))
-                localMediaRepository.updateCurrentConnectedDevice(bluetoothMediaDevice)
+                localMediaRepository.updateCurrentConnectedDevice(
+                    TestMediaDevicesFactory.bluetoothMediaDevice()
+                )
 
                 val device by collectLastValue(underTest.currentAudioDevice)
 
@@ -208,48 +218,8 @@
     }
 
     private companion object {
+
         val testIcon = TestStubDrawable()
-        val builtInDevice =
-            AudioDeviceInfo(
-                AudioDevicePort.createForTesting(
-                    AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
-                    "built_in",
-                    ""
-                )
-            )
-        val wiredDevice =
-            AudioDeviceInfo(
-                AudioDevicePort.createForTesting(AudioDeviceInfo.TYPE_WIRED_HEADPHONES, "wired", "")
-            )
-        val btDevice =
-            AudioDeviceInfo(
-                AudioDevicePort.createForTesting(
-                    AudioDeviceInfo.TYPE_BLE_HEADSET,
-                    "bt",
-                    "test_address"
-                )
-            )
-        val builtInMediaDevice: MediaDevice =
-            mock<PhoneMediaDevice> {
-                whenever(name).thenReturn("built_in_media")
-                whenever(icon).thenReturn(testIcon)
-            }
-        val wiredMediaDevice: MediaDevice =
-            mock<PhoneMediaDevice> {
-                whenever(deviceType)
-                    .thenReturn(MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE)
-                whenever(name).thenReturn("wired_media")
-                whenever(icon).thenReturn(testIcon)
-            }
-        val bluetoothMediaDevice: MediaDevice =
-            mock<BluetoothMediaDevice> {
-                whenever(name).thenReturn("bt_media")
-                whenever(icon).thenReturn(testIcon)
-                val cachedBluetoothDevice: CachedBluetoothDevice = mock {
-                    whenever(isHearingAidDevice).thenReturn(true)
-                }
-                whenever(cachedDevice).thenReturn(cachedBluetoothDevice)
-            }
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt
new file mode 100644
index 0000000..8921a23
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
+
+import android.graphics.drawable.TestStubDrawable
+import android.media.AudioManager
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.data.repository.TestAudioDevicesFactory
+import com.android.systemui.volume.data.repository.audioRepository
+import com.android.systemui.volume.data.repository.audioSharingRepository
+import com.android.systemui.volume.domain.model.AudioOutputDevice
+import com.android.systemui.volume.localMediaController
+import com.android.systemui.volume.localMediaRepository
+import com.android.systemui.volume.mediaControllerRepository
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaOutputComponentModel
+import com.android.systemui.volume.panel.shared.model.filterData
+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
+
+private const val builtInDeviceName = "This phone"
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class MediaOutputComponentInteractorTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+
+    private lateinit var underTest: MediaOutputComponentInteractor
+
+    @Before
+    fun setUp() =
+        with(kosmos) {
+            audioRepository.setMode(AudioManager.MODE_NORMAL)
+            localMediaRepository.updateCurrentConnectedDevice(
+                TestMediaDevicesFactory.builtInMediaDevice(deviceIcon = testIcon)
+            )
+
+            with(context.orCreateTestableResources) {
+                addOverride(R.drawable.ic_smartphone, testIcon)
+
+                addOverride(R.string.media_transfer_this_device_name_tv, builtInDeviceName)
+                addOverride(R.string.media_transfer_this_device_name_tablet, builtInDeviceName)
+                addOverride(R.string.media_transfer_this_device_name, builtInDeviceName)
+            }
+
+            underTest = mediaOutputComponentInteractor
+        }
+
+    @Test
+    fun inCall_stateIs_Calling() =
+        with(kosmos) {
+            testScope.runTest {
+                with(audioRepository) {
+                    setMode(AudioManager.MODE_IN_CALL)
+                    setCommunicationDevice(TestAudioDevicesFactory.builtInDevice())
+                }
+
+                val model by collectLastValue(underTest.mediaOutputModel.filterData())
+                runCurrent()
+
+                assertThat(model)
+                    .isEqualTo(
+                        MediaOutputComponentModel.Calling(
+                            AudioOutputDevice.BuiltIn(builtInDeviceName, testIcon),
+                            false,
+                        )
+                    )
+            }
+        }
+
+    @Test
+    fun hasSession_stateIs_MediaSession() =
+        with(kosmos) {
+            testScope.runTest {
+                mediaControllerRepository.setActiveSessions(listOf(localMediaController))
+
+                val model by collectLastValue(underTest.mediaOutputModel.filterData())
+                runCurrent()
+
+                with(model as MediaOutputComponentModel.MediaSession) {
+                    assertThat(session.appLabel).isEqualTo("local_media_controller_label")
+                    assertThat(session.packageName).isEqualTo("local.test.pkg")
+                    assertThat(session.canAdjustVolume).isTrue()
+                    assertThat(device)
+                        .isEqualTo(AudioOutputDevice.BuiltIn("built_in_media", testIcon))
+                    assertThat(isInAudioSharing).isFalse()
+                }
+            }
+        }
+
+    @Test
+    fun noMediaOrCall_stateIs_Idle() =
+        with(kosmos) {
+            testScope.runTest {
+                audioSharingRepository.setInAudioSharing(true)
+
+                val model by collectLastValue(underTest.mediaOutputModel.filterData())
+                runCurrent()
+
+                assertThat(model)
+                    .isEqualTo(
+                        MediaOutputComponentModel.Idle(
+                            AudioOutputDevice.BuiltIn("built_in_media", testIcon),
+                            true,
+                        )
+                    )
+            }
+        }
+
+    private companion object {
+        val testIcon = TestStubDrawable()
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
index d497b4a..86a20dc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
@@ -31,14 +31,11 @@
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
 import com.android.systemui.volume.data.repository.audioSharingRepository
-import com.android.systemui.volume.domain.interactor.audioModeInteractor
-import com.android.systemui.volume.domain.interactor.audioOutputInteractor
 import com.android.systemui.volume.localMediaController
 import com.android.systemui.volume.localMediaRepository
 import com.android.systemui.volume.mediaControllerRepository
-import com.android.systemui.volume.mediaDeviceSessionInteractor
 import com.android.systemui.volume.mediaOutputActionsInteractor
-import com.android.systemui.volume.mediaOutputInteractor
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.mediaOutputComponentInteractor
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
@@ -66,10 +63,7 @@
                     applicationContext,
                     testScope.backgroundScope,
                     mediaOutputActionsInteractor,
-                    mediaDeviceSessionInteractor,
-                    audioOutputInteractor,
-                    audioModeInteractor,
-                    mediaOutputInteractor,
+                    mediaOutputComponentInteractor,
                     uiEventLogger,
                 )
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/ui/navigation/VolumeNavigatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/ui/navigation/VolumeNavigatorTest.kt
new file mode 100644
index 0000000..7934b02
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/ui/navigation/VolumeNavigatorTest.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.ui.navigation
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.uiEventLoggerFake
+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.plugins.ActivityStarter
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.domain.model.VolumePanelRoute
+import com.android.systemui.volume.panel.domain.interactor.volumePanelGlobalStateInteractor
+import com.android.systemui.volume.panel.ui.viewmodel.volumePanelViewModelFactory
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class VolumeNavigatorTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+
+    private val underTest: VolumeNavigator =
+        with(kosmos) {
+            VolumeNavigator(
+                testScope.backgroundScope,
+                testDispatcher,
+                mock {},
+                activityStarter,
+                volumePanelViewModelFactory,
+                mock {
+                    on { create(any(), anyInt(), anyBoolean(), any()) }.thenReturn(mock {})
+                    on { applicationContext }.thenReturn(context)
+                },
+                uiEventLoggerFake,
+                volumePanelGlobalStateInteractor,
+            )
+        }
+
+    @Test
+    fun showNewVolumePanel_keyguardLocked_notShown() =
+        with(kosmos) {
+            testScope.runTest {
+                val panelState by collectLastValue(volumePanelGlobalStateInteractor.globalState)
+
+                underTest.openVolumePanel(VolumePanelRoute.COMPOSE_VOLUME_PANEL)
+                runCurrent()
+
+                assertThat(panelState!!.isVisible).isFalse()
+            }
+        }
+
+    @Test
+    fun showNewVolumePanel_keyguardUnlocked_shown() =
+        with(kosmos) {
+            testScope.runTest {
+                whenever(activityStarter.dismissKeyguardThenExecute(any(), any(), anyBoolean()))
+                    .then { (it.arguments[0] as ActivityStarter.OnDismissAction).onDismiss() }
+                val panelState by collectLastValue(volumePanelGlobalStateInteractor.globalState)
+
+                underTest.openVolumePanel(VolumePanelRoute.COMPOSE_VOLUME_PANEL)
+                runCurrent()
+
+                assertThat(panelState!!.isVisible).isTrue()
+            }
+        }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
index 072ec99..de65931 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ActivityStarter.java
@@ -70,9 +70,11 @@
     /**
      * Similar to {@link #startPendingIntentMaybeDismissingKeyguard(PendingIntent, Runnable,
      * ActivityTransitionAnimator.Controller)}, but also specifies a fill-in intent and extra
-     * options that could be used to populate the pending intent and launch the activity.
+     * option that could be used to populate the pending intent and launch the activity. This also
+     * allows the caller to avoid dismissing the shade.
      */
     void startPendingIntentMaybeDismissingKeyguard(PendingIntent intent,
+            boolean dismissShade,
             @Nullable Runnable intentSentUiThreadCallback,
             @Nullable ActivityTransitionAnimator.Controller animationController,
             @Nullable Intent fillInIntent,
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
index 629c96c..c7998f0 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
@@ -153,6 +153,9 @@
 
 /** Events that should call when various rendering parameters change */
 interface ClockEvents {
+    /** Set to enable or disable swipe interaction */
+    var isReactiveTouchInteractionEnabled: Boolean
+
     /** Call whenever timezone changes */
     fun onTimeZoneChanged(timeZone: TimeZone)
 
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
index 3244eb4..bf58eee 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
@@ -94,6 +94,9 @@
     default void setHasNotifications(boolean hasNotifications) {
     }
 
+    /** Sets whether the squishiness fraction should be updated on the media host. */
+    default void setShouldUpdateSquishinessOnMedia(boolean shouldUpdate) {}
+
     /**
      * Should touches from the notification panel be disallowed?
      * The notification panel might grab any touches rom QS at any time to collapse the shade.
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_spatial_speaker.xml b/packages/SystemUI/res-keyguard/drawable/ic_spatial_speaker.xml
new file mode 100644
index 0000000..82b222e7
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/ic_spatial_speaker.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/white"
+        android:pathData="M360,880q-134,0 -227,-93T40,560h80q0,100 70,170t170,70v80ZM360,740q-75,0 -127.5,-52.5T180,560h80q0,42 29,71t71,29v80ZM400,600q-33,0 -56.5,-23.5T320,520v-320q0,-33 23.5,-56.5T400,120h160q33,0 56.5,23.5T640,200v320q0,33 -23.5,56.5T560,600L400,600ZM400,520h160v-320L400,200v320ZM600,740v-80q42,0 71,-29t29,-71h80q0,75 -52.5,127.5T600,740ZM600,880v-80q100,0 170,-70t70,-170h80q0,134 -93,227T600,880ZM400,520h160,-160Z" />
+</vector>
diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions.xml b/packages/SystemUI/res-keyguard/layout/footer_actions.xml
deleted file mode 100644
index 4a2a1cb..0000000
--- a/packages/SystemUI/res-keyguard/layout/footer_actions.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-** Copyright 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.
--->
-
-<!-- Action buttons for footer in QS/QQS, containing settings button, power off button etc -->
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="@dimen/footer_actions_height"
-    android:elevation="@dimen/qs_panel_elevation"
-    android:paddingTop="@dimen/qs_footer_actions_top_padding"
-    android:paddingBottom="@dimen/qs_footer_actions_bottom_padding"
-    android:background="@drawable/qs_footer_actions_background"
-    android:gravity="center_vertical|end"
-    android:layout_gravity="bottom"
-/>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions_icon_button.xml b/packages/SystemUI/res-keyguard/layout/footer_actions_icon_button.xml
deleted file mode 100644
index fad41c82..0000000
--- a/packages/SystemUI/res-keyguard/layout/footer_actions_icon_button.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/qs_footer_action_button_size"
-    android:layout_height="@dimen/qs_footer_action_button_size"
-    android:visibility="gone">
-    <ImageView
-        android:id="@+id/icon"
-        android:layout_width="@dimen/qs_footer_icon_size"
-        android:layout_height="@dimen/qs_footer_icon_size"
-        android:layout_gravity="center"
-        android:scaleType="centerInside" />
-</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions_number_button.xml b/packages/SystemUI/res-keyguard/layout/footer_actions_number_button.xml
deleted file mode 100644
index c09607d..0000000
--- a/packages/SystemUI/res-keyguard/layout/footer_actions_number_button.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/qs_footer_action_button_size"
-    android:layout_height="@dimen/qs_footer_action_button_size"
-    android:background="@drawable/qs_footer_action_circle"
-    android:visibility="gone">
-    <TextView
-        android:id="@+id/number"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textAppearance="@style/TextAppearance.QS.SecurityFooter"
-        android:layout_gravity="center"
-        android:textColor="?attr/onShadeInactiveVariant"
-        android:textSize="18sp"/>
-    <ImageView
-        android:id="@+id/new_dot"
-        android:layout_width="12dp"
-        android:layout_height="12dp"
-        android:scaleType="fitCenter"
-        android:layout_gravity="bottom|end"
-        android:src="@drawable/fgs_dot"
-        android:contentDescription="@string/fgs_dot_content_description" />
-</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml b/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml
deleted file mode 100644
index 1c31f1d..0000000
--- a/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     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.
--->
-<com.android.systemui.animation.view.LaunchableLinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="0dp"
-    android:layout_height="@dimen/qs_security_footer_single_line_height"
-    android:layout_weight="1"
-    android:orientation="horizontal"
-    android:paddingHorizontal="@dimen/qs_footer_padding"
-    android:gravity="center_vertical"
-    android:layout_marginEnd="@dimen/qs_footer_action_inset"
-    android:background="@drawable/qs_security_footer_background"
-    android:visibility="gone">
-    <ImageView
-        android:id="@+id/icon"
-        android:layout_width="@dimen/qs_footer_icon_size"
-        android:layout_height="@dimen/qs_footer_icon_size"
-        android:gravity="start"
-        android:layout_marginEnd="12dp"
-        android:contentDescription="@null"
-        android:src="@drawable/ic_info_outline"
-        android:tint="?attr/onSurfaceVariant" />
-
-    <TextView
-        android:id="@+id/text"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:maxLines="1"
-        android:ellipsize="end"
-        android:textAppearance="@style/TextAppearance.QS.SecurityFooter"
-        android:textColor="?attr/onSurfaceVariant"/>
-
-    <ImageView
-        android:id="@+id/new_dot"
-        android:layout_width="12dp"
-        android:layout_height="12dp"
-        android:scaleType="fitCenter"
-        android:src="@drawable/fgs_dot"
-        android:contentDescription="@string/fgs_dot_content_description"
-        />
-
-    <ImageView
-        android:id="@+id/chevron_icon"
-        android:layout_width="@dimen/qs_footer_icon_size"
-        android:layout_height="@dimen/qs_footer_icon_size"
-        android:layout_marginStart="8dp"
-        android:contentDescription="@null"
-        android:src="@*android:drawable/ic_chevron_end"
-        android:autoMirrored="true"
-        android:tint="?attr/onSurfaceVariant" />
-</com.android.systemui.animation.view.LaunchableLinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/qs_footer_power_button_overlay_color.xml b/packages/SystemUI/res/color/qs_footer_power_button_overlay_color.xml
deleted file mode 100644
index a8abd79..0000000
--- a/packages/SystemUI/res/color/qs_footer_power_button_overlay_color.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2023 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_pressed="true" android:color="?attr/onShadeActive" android:alpha="0.12" />
-    <item android:state_hovered="true" android:color="?attr/onShadeActive" android:alpha="0.09" />
-    <item android:color="@color/transparent" />
-</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/fgs_dot.xml b/packages/SystemUI/res/drawable/fgs_dot.xml
deleted file mode 100644
index 0881d7c..0000000
--- a/packages/SystemUI/res/drawable/fgs_dot.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-** Copyright 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.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-    android:shape="oval"
-    android:width="12dp"
-    android:height="12dp">
-    <solid android:color="?attr/tertiary" />
-</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_widgets.xml b/packages/SystemUI/res/drawable/ic_widgets.xml
new file mode 100644
index 0000000..b21d047
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_widgets.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<!-- go/gm2-icons, from gs_widgets_vd_theme_24.xml -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?attr/colorControlNormal"
+    android:viewportHeight="960"
+    android:viewportWidth="960">
+    <path
+        android:fillColor="@android:color/black"
+        android:pathData="M666,520L440,294L666,68L892,294L666,520ZM120,440L120,120L440,120L440,440L120,440ZM520,840L520,520L840,520L840,840L520,840ZM120,840L120,520L440,520L440,840L120,840ZM200,360L360,360L360,200L200,200L200,360ZM667,408L780,295L667,182L554,295L667,408ZM600,760L760,760L760,600L600,600L600,760ZM200,760L360,760L360,600L200,600L200,760ZM360,360L360,360L360,360L360,360L360,360ZM554,295L554,295L554,295L554,295L554,295ZM360,600L360,600L360,600L360,600L360,600ZM600,600L600,600L600,600L600,600L600,600Z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_circle.xml b/packages/SystemUI/res/drawable/qs_footer_action_circle.xml
deleted file mode 100644
index 4a5d4af..0000000
--- a/packages/SystemUI/res/drawable/qs_footer_action_circle.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ 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.
-  -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-       android:inset="@dimen/qs_footer_action_inset">
-    <ripple
-        android:color="?android:attr/colorControlHighlight">
-        <item android:id="@android:id/mask">
-            <!-- We make this shape a rounded rectangle instead of a oval so that it can animate -->
-            <!-- properly into an app/dialog. -->
-            <shape android:shape="rectangle">
-                <solid android:color="@android:color/white"/>
-                <corners android:radius="@dimen/qs_footer_action_corner_radius"/>
-            </shape>
-        </item>
-        <item>
-            <shape android:shape="rectangle">
-                <solid android:color="?attr/shadeInactive"/>
-                <corners android:radius="@dimen/qs_footer_action_corner_radius"/>
-            </shape>
-        </item>
-
-    </ripple>
-</inset>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml b/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
deleted file mode 100644
index 47a2965..0000000
--- a/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ 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.
-  -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-    android:inset="@dimen/qs_footer_action_inset">
-    <ripple
-        android:color="?android:attr/colorControlHighlight">
-        <item android:id="@android:id/mask">
-            <!-- We make this shape a rounded rectangle instead of a oval so that it can animate -->
-            <!-- properly into an app/dialog. -->
-            <shape android:shape="rectangle">
-                <solid android:color="@android:color/white"/>
-                <corners android:radius="@dimen/qs_footer_action_corner_radius"/>
-            </shape>
-        </item>
-        <item>
-            <shape android:shape="rectangle">
-                <solid android:color="?attr/shadeActive"/>
-                <corners android:radius="@dimen/qs_footer_action_corner_radius"/>
-            </shape>
-        </item>
-        <item>
-            <shape android:shape="rectangle">
-                <solid android:color="@color/qs_footer_power_button_overlay_color"/>
-                <corners android:radius="@dimen/qs_footer_action_corner_radius"/>
-            </shape>
-        </item>
-
-    </ripple>
-</inset>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml b/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml
new file mode 100644
index 0000000..627b92b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_hearing_devices_related_tools_background.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+  -->
+
+<ripple
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:color="?android:attr/colorControlHighlight">
+    <item>
+        <shape android:shape="rectangle">
+            <solid android:color="@android:color/transparent"/>
+            <corners android:radius="@dimen/hearing_devices_preset_spinner_background_radius"/>
+            <stroke
+                android:width="1dp"
+                android:color="?androidprv:attr/textColorTertiary" />
+        </shape>
+    </item>
+</ripple>
diff --git a/packages/SystemUI/res/drawable/qs_security_footer_background.xml b/packages/SystemUI/res/drawable/qs_security_footer_background.xml
deleted file mode 100644
index 0b0055b..0000000
--- a/packages/SystemUI/res/drawable/qs_security_footer_background.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2021 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
-    android:insetTop="@dimen/qs_footer_action_inset"
-    android:insetBottom="@dimen/qs_footer_action_inset"
-    >
-    <ripple
-        android:color="?android:attr/colorControlHighlight">
-        <item android:id="@android:id/mask">
-            <shape android:shape="rectangle">
-                <solid android:color="@android:color/white"/>
-                <corners android:radius="@dimen/qs_security_footer_corner_radius"/>
-            </shape>
-        </item>
-        <item>
-            <shape android:shape="rectangle">
-                <stroke android:width="1dp"
-                        android:color="?attr/shadeInactive"/>
-                <corners android:radius="@dimen/qs_security_footer_corner_radius"/>
-            </shape>
-        </item>
-    </ripple>
-</inset>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/rounded_bg_full_large_radius.xml b/packages/SystemUI/res/drawable/rounded_bg_full_large_radius.xml
deleted file mode 100644
index 29a014a..0000000
--- a/packages/SystemUI/res/drawable/rounded_bg_full_large_radius.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2021 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-    android:shape="rectangle">
-    <solid android:color="?androidprv:attr/colorAccentPrimary" />
-    <corners android:radius="40dp" />
-</shape>
diff --git a/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
deleted file mode 100644
index 8b9eabc..0000000
--- a/packages/SystemUI/res/layout-sw600dp/biometric_prompt_constraint_layout.xml
+++ /dev/null
@@ -1,237 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-    android:id="@+id/biometric_prompt_constraint_layout"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <ImageView
-        android:id="@+id/background"
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:contentDescription="@string/biometric_dialog_empty_space_description"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent" />
-
-    <View
-        android:id="@+id/panel"
-        style="@style/AuthCredentialPanelStyle"
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toStartOf="@+id/rightGuideline"
-        app:layout_constraintStart_toStartOf="@+id/leftGuideline"
-        app:layout_constraintTop_toTopOf="@+id/topBarrier"
-        app:layout_constraintWidth_max="640dp" />
-
-    <include
-        android:id="@+id/button_bar"
-        layout="@layout/biometric_prompt_button_bar"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        app:layout_constraintBottom_toBottomOf="@id/bottomGuideline"
-        app:layout_constraintEnd_toEndOf="@id/panel"
-        app:layout_constraintStart_toStartOf="@id/panel" />
-
-    <ScrollView
-        android:id="@+id/scrollView"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:fadeScrollbars="false"
-        android:fillViewport="true"
-        android:paddingBottom="32dp"
-        android:paddingHorizontal="32dp"
-        android:paddingTop="24dp"
-        app:layout_constrainedHeight="true"
-        app:layout_constrainedWidth="true"
-        app:layout_constraintBottom_toTopOf="@+id/scrollBarrier"
-        app:layout_constraintEnd_toEndOf="@id/panel"
-        app:layout_constraintStart_toStartOf="@id/panel"
-        app:layout_constraintTop_toTopOf="@+id/topGuideline"
-        app:layout_constraintVertical_bias="1.0">
-
-        <androidx.constraintlayout.widget.ConstraintLayout
-            android:id="@+id/innerConstraint"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content">
-
-            <ImageView
-                android:id="@+id/logo"
-                android:layout_width="@dimen/biometric_prompt_logo_size"
-                android:layout_height="@dimen/biometric_prompt_logo_size"
-                android:layout_gravity="center"
-                android:contentDescription="@string/biometric_dialog_logo"
-                android:scaleType="fitXY"
-                android:visibility="visible"
-                app:layout_constraintBottom_toTopOf="@+id/logo_description"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintTop_toTopOf="parent" />
-
-            <TextView
-                android:id="@+id/logo_description"
-                style="@style/TextAppearance.AuthCredential.LogoDescription"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:paddingTop="16dp"
-                app:layout_constraintBottom_toTopOf="@+id/title"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintTop_toBottomOf="@+id/logo" />
-
-            <TextView
-                android:id="@+id/title"
-                style="@style/TextAppearance.AuthCredential.Title"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:gravity="@integer/biometric_dialog_text_gravity"
-                android:paddingTop="16dp"
-                app:layout_constraintBottom_toTopOf="@+id/subtitle"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintTop_toBottomOf="@+id/logo_description" />
-
-            <TextView
-                android:id="@+id/subtitle"
-                style="@style/TextAppearance.AuthCredential.Subtitle"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:gravity="@integer/biometric_dialog_text_gravity"
-                android:paddingTop="16dp"
-                app:layout_constraintBottom_toTopOf="@+id/contentBarrier"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintTop_toBottomOf="@+id/title" />
-
-            <LinearLayout
-                android:id="@+id/customized_view_container"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:gravity="center_vertical"
-                android:orientation="vertical"
-                android:paddingTop="24dp"
-                android:visibility="gone"
-                app:layout_constraintBottom_toBottomOf="parent"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintTop_toBottomOf="@+id/subtitle" />
-
-            <TextView
-                android:id="@+id/description"
-                style="@style/TextAppearance.AuthCredential.Description"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:gravity="@integer/biometric_dialog_text_gravity"
-                android:paddingTop="16dp"
-                android:textAlignment="viewStart"
-                app:layout_constraintBottom_toBottomOf="parent"
-                app:layout_constraintEnd_toEndOf="parent"
-                app:layout_constraintStart_toStartOf="parent"
-                app:layout_constraintTop_toBottomOf="@+id/subtitle" />
-
-            <androidx.constraintlayout.widget.Barrier
-                android:id="@+id/contentBarrier"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                app:barrierAllowsGoneWidgets="false"
-                app:barrierDirection="top"
-                app:constraint_referenced_ids="description, customized_view_container" />
-
-        </androidx.constraintlayout.widget.ConstraintLayout>
-    </ScrollView>
-
-    <!-- Cancel Button, replaces negative button when biometric is accepted -->
-    <TextView
-        android:id="@+id/indicator"
-        style="@style/TextAppearance.AuthCredential.Indicator"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="24dp"
-        android:accessibilityLiveRegion="assertive"
-        android:fadingEdge="horizontal"
-        android:gravity="center_horizontal"
-        android:scrollHorizontally="true"
-        app:layout_constraintBottom_toTopOf="@+id/button_bar"
-        app:layout_constraintEnd_toEndOf="@+id/panel"
-        app:layout_constraintStart_toStartOf="@+id/panel"
-        app:layout_constraintTop_toBottomOf="@+id/biometric_icon"
-        app:layout_constraintVertical_bias="0.0" />
-
-    <!-- "Use Credential" Button, replaces if device credential is allowed -->
-
-    <!-- Positive Button -->
-    <androidx.constraintlayout.widget.Barrier
-        android:id="@+id/topBarrier"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        app:barrierAllowsGoneWidgets="false"
-        app:barrierDirection="top"
-        app:constraint_referenced_ids="scrollView" />
-
-    <!-- Try Again Button -->
-    <androidx.constraintlayout.widget.Barrier
-        android:id="@+id/scrollBarrier"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        app:barrierAllowsGoneWidgets="false"
-        app:barrierDirection="top"
-        app:constraint_referenced_ids="biometric_icon, button_bar" />
-
-    <!-- Guidelines for setting panel border -->
-    <androidx.constraintlayout.widget.Guideline
-        android:id="@+id/leftGuideline"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-        app:layout_constraintGuide_begin="@dimen/biometric_dialog_border_padding" />
-
-    <androidx.constraintlayout.widget.Guideline
-        android:id="@+id/rightGuideline"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="vertical"
-        app:layout_constraintGuide_end="@dimen/biometric_dialog_border_padding" />
-
-    <androidx.constraintlayout.widget.Guideline
-        android:id="@+id/bottomGuideline"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:orientation="horizontal"
-        app:layout_constraintGuide_end="40dp" />
-
-    <androidx.constraintlayout.widget.Guideline
-        android:id="@+id/topGuideline"
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:orientation="horizontal"
-        app:layout_constraintGuide_begin="56dp" />
-
-    <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
-        android:id="@+id/biometric_icon"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintVertical_bias="1.0"
-        tools:srcCompat="@tools:sample/avatars" />
-
-    <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
-        android:id="@+id/biometric_icon_overlay"
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:layout_gravity="center"
-        android:contentDescription="@null"
-        android:scaleType="fitXY"
-        android:importantForAccessibility="no"
-        app:layout_constraintBottom_toBottomOf="@+id/biometric_icon"
-        app:layout_constraintEnd_toEndOf="@+id/biometric_icon"
-        app:layout_constraintStart_toStartOf="@+id/biometric_icon"
-        app:layout_constraintTop_toTopOf="@+id/biometric_icon" />
-
-</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/packages/SystemUI/res/layout/activity_keyboard_shortcut_helper.xml b/packages/SystemUI/res/layout/activity_keyboard_shortcut_helper.xml
index 292e496..06d1bf4 100644
--- a/packages/SystemUI/res/layout/activity_keyboard_shortcut_helper.xml
+++ b/packages/SystemUI/res/layout/activity_keyboard_shortcut_helper.xml
@@ -5,9 +5,9 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <LinearLayout
+    <FrameLayout
         android:id="@+id/shortcut_helper_sheet"
-        style="@style/Widget.Material3.BottomSheet"
+        style="@style/ShortcutHelperBottomSheet"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="vertical"
@@ -19,13 +19,9 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content" />
 
-        <TextView
+        <androidx.compose.ui.platform.ComposeView
+            android:id="@+id/shortcut_helper_compose_container"
             android:layout_width="match_parent"
-            android:layout_height="0dp"
-            android:layout_weight="1"
-            android:gravity="center"
-            android:textAppearance="?textAppearanceDisplayLarge"
-            android:background="?colorTertiaryContainer"
-            android:text="Shortcut Helper Content" />
-    </LinearLayout>
-</androidx.coordinatorlayout.widget.CoordinatorLayout>
+            android:layout_height="match_parent" />
+    </FrameLayout>
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
similarity index 92%
rename from packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
rename to packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
index 9b5b59f..8d50bfa 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_constraint_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_one_pane_layout.xml
@@ -22,9 +22,11 @@
         android:layout_width="0dp"
         android:layout_height="0dp"
         app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintEnd_toEndOf="@id/rightGuideline"
+        app:layout_constraintEnd_toStartOf="@id/rightGuideline"
         app:layout_constraintStart_toStartOf="@id/leftGuideline"
-        app:layout_constraintTop_toTopOf="@id/topBarrier" />
+        app:layout_constraintTop_toTopOf="@id/topBarrier"
+        app:layout_constraintWidth_max="@dimen/biometric_prompt_panel_max_width" />
+
 
     <include
         android:id="@+id/button_bar"
@@ -41,8 +43,8 @@
         android:layout_height="wrap_content"
         android:fillViewport="true"
         android:fadeScrollbars="false"
-        android:paddingBottom="24dp"
-        android:paddingHorizontal="24dp"
+        android:paddingBottom="@dimen/biometric_prompt_top_scroll_view_bottom_padding"
+        android:paddingHorizontal="@dimen/biometric_prompt_top_scroll_view_horizontal_padding"
         android:paddingTop="24dp"
         app:layout_constrainedHeight="true"
         app:layout_constrainedWidth="true"
@@ -76,7 +78,7 @@
                 style="@style/TextAppearance.AuthCredential.LogoDescription"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:paddingTop="8dp"
+                android:paddingTop="@dimen/biometric_prompt_logo_description_top_padding"
                 app:layout_constraintBottom_toTopOf="@+id/title"
                 app:layout_constraintEnd_toEndOf="parent"
                 app:layout_constraintStart_toStartOf="parent"
@@ -180,14 +182,14 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:orientation="vertical"
-        app:layout_constraintGuide_begin="0dp" />
+        app:layout_constraintGuide_begin="@dimen/biometric_prompt_one_pane_medium_horizontal_guideline_padding" />
 
     <androidx.constraintlayout.widget.Guideline
         android:id="@+id/rightGuideline"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:orientation="vertical"
-        app:layout_constraintGuide_end="0dp" />
+        app:layout_constraintGuide_end="@dimen/biometric_prompt_one_pane_medium_horizontal_guideline_padding" />
 
     <androidx.constraintlayout.widget.Guideline
         android:id="@+id/bottomGuideline"
@@ -201,7 +203,7 @@
         android:layout_width="0dp"
         android:layout_height="0dp"
         android:orientation="horizontal"
-        app:layout_constraintGuide_begin="119dp" />
+        app:layout_constraintGuide_begin="@dimen/biometric_prompt_one_pane_medium_top_guideline_padding" />
 
     <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper
         android:id="@+id/biometric_icon"
diff --git a/packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
similarity index 100%
rename from packages/SystemUI/res/layout-land/biometric_prompt_constraint_layout.xml
rename to packages/SystemUI/res/layout/biometric_prompt_two_pane_layout.xml
diff --git a/packages/SystemUI/res/layout/dream_overlay_open_hub_chip.xml b/packages/SystemUI/res/layout/dream_overlay_open_hub_chip.xml
new file mode 100644
index 0000000..be063a9
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_open_hub_chip.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+-->
+<com.android.systemui.animation.view.LaunchableImageView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_height="@dimen/dream_overlay_bottom_affordance_height"
+    android:layout_width="@dimen/dream_overlay_bottom_affordance_width"
+    android:layout_gravity="bottom|start"
+    android:padding="@dimen/dream_overlay_bottom_affordance_padding"
+    android:scaleType="fitCenter"
+    android:tint="?android:attr/textColorPrimary"
+    android:src="@drawable/ic_widgets"
+    android:contentDescription="@string/accessibility_action_open_communal_hub" />
diff --git a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
index 2bf6f80..4a7bef9 100644
--- a/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
+++ b/packages/SystemUI/res/layout/hearing_devices_tile_dialog.xml
@@ -36,9 +36,8 @@
         style="@style/BluetoothTileDialog.Device"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:minHeight="@dimen/hearing_devices_preset_spinner_height"
         android:layout_marginTop="@dimen/hearing_devices_layout_margin"
-        android:layout_marginBottom="@dimen/hearing_devices_layout_margin"
+        android:minHeight="@dimen/hearing_devices_preset_spinner_height"
         android:gravity="center_vertical"
         android:background="@drawable/hearing_devices_preset_spinner_background"
         android:popupBackground="@drawable/hearing_devices_preset_spinner_popup_background"
@@ -54,9 +53,10 @@
         android:visibility="gone"/>
 
     <androidx.constraintlayout.widget.Barrier
-        android:id="@+id/device_barrier"
+        android:id="@+id/device_function_barrier"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        app:barrierAllowsGoneWidgets="false"
         app:barrierDirection="bottom"
         app:constraint_referenced_ids="device_list,preset_spinner" />
 
@@ -71,7 +71,8 @@
         android:contentDescription="@string/accessibility_hearing_device_pair_new_device"
         app:layout_constraintStart_toStartOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/device_barrier"
+        app:layout_constraintTop_toBottomOf="@id/device_function_barrier"
+        app:layout_constraintBottom_toTopOf="@id/related_tools_scroll"
         android:drawableStart="@drawable/ic_add"
         android:drawablePadding="20dp"
         android:drawableTint="?android:attr/textColorPrimary"
@@ -83,4 +84,32 @@
         android:maxLines="1"
         android:ellipsize="end" />
 
+    <androidx.constraintlayout.widget.Barrier
+        android:id="@+id/device_barrier"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        app:barrierAllowsGoneWidgets="false"
+        app:barrierDirection="bottom"
+        app:constraint_referenced_ids="device_function_barrier, pair_new_device_button" />
+
+    <HorizontalScrollView
+        android:id="@+id/related_tools_scroll"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/bluetooth_dialog_layout_margin"
+        android:layout_marginEnd="@dimen/bluetooth_dialog_layout_margin"
+        android:layout_marginTop="@dimen/hearing_devices_layout_margin"
+        android:scrollbars="none"
+        android:fillViewport="true"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toBottomOf="@id/preset_spinner">
+        <LinearLayout
+            android:id="@+id/related_tools_container"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="horizontal">
+        </LinearLayout>
+    </HorizontalScrollView>
+
 </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/hearing_tool_item.xml b/packages/SystemUI/res/layout/hearing_tool_item.xml
new file mode 100644
index 0000000..84462d0
--- /dev/null
+++ b/packages/SystemUI/res/layout/hearing_tool_item.xml
@@ -0,0 +1,53 @@
+<!--
+    Copyright (C) 2024 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/tool_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:gravity="center"
+    android:focusable="true"
+    android:clickable="true"
+    android:layout_weight="1">
+    <FrameLayout
+        android:id="@+id/icon_frame"
+        android:layout_width="@dimen/hearing_devices_tool_icon_frame_width"
+        android:layout_height="@dimen/hearing_devices_tool_icon_frame_height"
+        android:background="@drawable/qs_hearing_devices_related_tools_background"
+        android:focusable="false" >
+        <ImageView
+            android:id="@+id/tool_icon"
+            android:layout_width="@dimen/hearing_devices_tool_icon_size"
+            android:layout_height="@dimen/hearing_devices_tool_icon_size"
+            android:layout_gravity="center"
+            android:scaleType="fitCenter"
+            android:focusable="false" />
+    </FrameLayout>
+    <TextView
+        android:id="@+id/tool_name"
+        android:textDirection="locale"
+        android:textAlignment="center"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/hearing_devices_layout_margin"
+        android:ellipsize="end"
+        android:maxLines="1"
+        android:textSize="12sp"
+        android:textAppearance="@style/TextAppearance.Dialog.Body.Message"
+        android:focusable="false" />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_activity_chip.xml b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
index a33be12..cd5c37d 100644
--- a/packages/SystemUI/res/layout/ongoing_activity_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_activity_chip.xml
@@ -40,6 +40,7 @@
 
         <ImageView
             android:src="@*android:drawable/ic_phone"
+            android:id="@+id/ongoing_activity_chip_icon"
             android:layout_width="@dimen/ongoing_activity_chip_icon_size"
             android:layout_height="@dimen/ongoing_activity_chip_icon_size"
             android:tint="?android:attr/colorPrimary"
diff --git a/packages/SystemUI/res/layout/people_space_activity.xml b/packages/SystemUI/res/layout/people_space_activity.xml
deleted file mode 100644
index f45cc7c..0000000
--- a/packages/SystemUI/res/layout/people_space_activity.xml
+++ /dev/null
@@ -1,23 +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.
-  -->
-<FrameLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/container"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-    <!-- The content of people_space_activity_(no|with)_conversations.xml will be added here at
-         runtime depending on the number of conversations to show. -->
-</FrameLayout>
diff --git a/packages/SystemUI/res/layout/people_space_activity_list_divider.xml b/packages/SystemUI/res/layout/people_space_activity_list_divider.xml
deleted file mode 100644
index 3b9fb3b..0000000
--- a/packages/SystemUI/res/layout/people_space_activity_list_divider.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2021 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<View
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="2dp"
-    android:background="?android:attr/colorBackground" />
diff --git a/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml b/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml
deleted file mode 100644
index a97c90c..0000000
--- a/packages/SystemUI/res/layout/people_space_activity_no_conversations.xml
+++ /dev/null
@@ -1,79 +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.
-  -->
-<RelativeLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-    android:id="@+id/top_level_no_conversations"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:padding="24dp"
-    android:clipToOutline="true">
-    <TextView
-        android:id="@+id/select_conversation_title"
-        android:gravity="center"
-        android:text="@string/select_conversation_title"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_centerHorizontal="true"
-        android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
-        android:textColor="?android:attr/textColorPrimary"
-        android:textSize="24sp"
-        android:layout_alignParentTop="true" />
-
-    <TextView
-        android:id="@+id/select_conversation"
-        android:gravity="center"
-        android:text="@string/no_conversations_text"
-        android:layout_width="match_parent"
-        android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
-        android:textColor="?android:attr/textColorPrimary"
-        android:textSize="16sp"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal"
-        android:padding="24dp"
-        android:layout_marginTop="26dp"
-        android:layout_below="@id/select_conversation_title"/>
-
-    <Button
-        style="?android:attr/buttonBarButtonStyle"
-        android:id="@+id/got_it_button"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:background="@drawable/rounded_bg_full_large_radius"
-        android:text="@string/got_it"
-        android:textColor="?androidprv:attr/textColorOnAccent"
-        android:layout_marginBottom="60dp"
-        android:layout_alignParentBottom="true" />
-
-    <LinearLayout
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_above="@id/got_it_button"
-        android:layout_below="@id/select_conversation"
-        android:layout_centerInParent="true"
-        android:clipToOutline="true">
-        <LinearLayout
-            android:id="@+id/widget_initial_layout"
-            android:layout_width="200dp"
-            android:layout_height="100dp"
-            android:layout_gravity="center"
-            android:background="@drawable/rounded_bg_full_large_radius"
-            android:layout_above="@id/got_it_button">
-            <include layout="@layout/people_space_placeholder_layout" />
-        </LinearLayout>
-    </LinearLayout>
-</RelativeLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_space_activity_with_conversations.xml b/packages/SystemUI/res/layout/people_space_activity_with_conversations.xml
deleted file mode 100644
index 2384963..0000000
--- a/packages/SystemUI/res/layout/people_space_activity_with_conversations.xml
+++ /dev/null
@@ -1,115 +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.
-  -->
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-    android:id="@+id/top_level_with_conversations"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical"
-    android:padding="8dp">
-    <TextView
-        android:id="@+id/select_conversation_title"
-        android:text="@string/select_conversation_title"
-        android:gravity="center"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal"
-        android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
-        android:textColor="?android:attr/textColorPrimary"
-        android:textSize="24sp"/>
-
-    <TextView
-        android:id="@+id/select_conversation"
-        android:text="@string/select_conversation_text"
-        android:gravity="center"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center_horizontal"
-        android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
-        android:textColor="?android:attr/textColorPrimary"
-        android:textSize="16sp"
-        android:paddingVertical="24dp"
-        android:paddingHorizontal="48dp"/>
-
-    <androidx.core.widget.NestedScrollView
-        android:id="@+id/scroll_view"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent">
-
-        <LinearLayout
-            android:id="@+id/scroll_layout"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="16dp"
-            android:orientation="vertical">
-
-            <LinearLayout
-                android:id="@+id/priority"
-                android:orientation="vertical"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginBottom="35dp">
-                <TextView
-                    android:id="@+id/priority_header"
-                    android:text="@string/priority_conversations"
-                    android:layout_width="wrap_content"
-                    android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title"
-                    android:textColor="?androidprv:attr/colorAccentPrimaryVariant"
-                    android:textSize="14sp"
-                    android:paddingStart="16dp"
-                    android:layout_height="wrap_content"/>
-
-                <LinearLayout
-                    android:id="@+id/priority_tiles"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:layout_marginTop="10dp"
-                    android:orientation="vertical"
-                    android:background="@drawable/rounded_bg_full_large_radius"
-                    android:clipToOutline="true">
-                </LinearLayout>
-            </LinearLayout>
-
-            <LinearLayout
-                android:id="@+id/recent"
-                android:orientation="vertical"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content">
-                <TextView
-                    android:id="@+id/recent_header"
-                    android:gravity="start"
-                    android:text="@string/recent_conversations"
-                    android:layout_width="wrap_content"
-                    android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Notification.Title"
-                    android:textColor="?androidprv:attr/colorAccentPrimaryVariant"
-                    android:textSize="14sp"
-                    android:paddingStart="16dp"
-                    android:layout_height="wrap_content"/>
-
-                <LinearLayout
-                    android:id="@+id/recent_tiles"
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:layout_marginTop="10dp"
-                    android:orientation="vertical"
-                    android:background="@drawable/rounded_bg_full_large_radius"
-                    android:clipToOutline="true">
-                </LinearLayout>
-            </LinearLayout>
-        </LinearLayout>
-    </androidx.core.widget.NestedScrollView>
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_space_tile_view.xml b/packages/SystemUI/res/layout/people_space_tile_view.xml
deleted file mode 100644
index b0599ca..0000000
--- a/packages/SystemUI/res/layout/people_space_tile_view.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ 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.
-  -->
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
-    android:id="@+id/tile_view"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:orientation="vertical">
-
-    <LinearLayout
-        android:orientation="vertical"
-        android:background="?androidprv:attr/colorSurface"
-        android:padding="12dp"
-        android:elevation="4dp"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content">
-
-        <LinearLayout
-            android:orientation="horizontal"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:gravity="start">
-
-            <ImageView
-                android:id="@+id/tile_view_person_icon"
-                android:layout_width="@dimen/avatar_size_for_medium"
-                android:layout_height="@dimen/avatar_size_for_medium" />
-
-            <LinearLayout
-                android:orientation="horizontal"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_gravity="center_vertical">
-
-                <TextView
-                    android:id="@+id/tile_view_name"
-                    android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
-                    android:paddingHorizontal="16dp"
-                    android:textSize="22sp"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="center_vertical"/>
-            </LinearLayout>
-        </LinearLayout>
-    </LinearLayout>
-</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index e3c5a7d..5f77f61 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -47,13 +47,12 @@
 
     <include layout="@layout/quick_status_bar_expanded_header" />
 
-    <include
-        layout="@layout/footer_actions"
+    <androidx.compose.ui.platform.ComposeView
         android:id="@+id/qs_footer_actions"
         android:layout_height="@dimen/footer_actions_height"
         android:layout_width="match_parent"
         android:layout_gravity="bottom"
-        />
+        android:elevation="@dimen/qs_panel_elevation" />
 
     <include
         android:id="@+id/qs_customize"
diff --git a/packages/SystemUI/res/raw/face_dialog_authenticating.json b/packages/SystemUI/res/raw/face_dialog_authenticating.json
deleted file mode 100644
index 4e25e6d..0000000
--- a/packages/SystemUI/res/raw/face_dialog_authenticating.json
+++ /dev/null
@@ -1 +0,0 @@
-{"v":"5.7.13","fr":60,"ip":0,"op":61,"w":64,"h":64,"nm":"face_scanning 3","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[32,32,0],"ix":2,"l":2},"a":{"a":0,"k":[27.25,27.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":0,"s":[100,100,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":30,"s":[95,95,100]},{"t":60,"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,-1.243],[-1.244,0],[0,1.243],[1.242,0]],"o":[[0,1.243],[1.242,0],[0,-1.243],[-1.244,0]],"v":[[-2.249,0.001],[0.001,2.251],[2.249,0.001],[0.001,-2.251]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,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]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[15.1,20.495],"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 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.243],[-1.242,0],[0,1.243],[1.242,0]],"o":[[0,1.243],[1.242,0],[0,-1.243],[-1.242,0]],"v":[[-2.249,0],[0.001,2.25],[2.249,0],[0.001,-2.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,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]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[39.4,20.495],"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":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[2.814,3.523],[-2.814,3.523],[-2.814,1.363],[0.652,1.363],[0.652,-3.523],[2.814,-3.523]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,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]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.791,28.479],"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 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.154,0.15],[0,0],[0.117,-0.095],[0,0],[0.228,-0.121],[0.358,-0.103],[0.922,0.261],[0.3,0.16],[0.24,0.185],[0.14,0.139],[0.178,0.261],[0.143,0.451],[0,0],[0,0.494],[0,0],[-0.214,-0.676],[-0.392,-0.572],[-0.323,-0.317],[-0.228,-0.177],[-0.333,-0.179],[-0.503,-0.145],[-0.662,0],[-0.653,0.184],[-0.437,0.233],[-0.336,0.258],[0,0],[0,0]],"o":[[0,0],[-0.107,0.106],[0,0],[-0.24,0.185],[-0.301,0.16],[-0.92,0.261],[-0.357,-0.103],[-0.228,-0.121],[-0.158,-0.122],[-0.225,-0.221],[-0.272,-0.393],[0,0],[-0.147,-0.466],[0,0],[0,0.716],[0.206,0.656],[0.256,0.372],[0.204,0.201],[0.336,0.258],[0.436,0.233],[0.655,0.184],[0.662,0],[0.503,-0.145],[0.332,-0.179],[0,0],[0,0],[0.165,-0.136]],"v":[[6.094,1.465],[4.579,-0.076],[4.242,0.225],[4.124,0.315],[3.43,0.771],[2.439,1.165],[-0.342,1.165],[-1.331,0.771],[-2.027,0.315],[-2.48,-0.075],[-3.087,-0.801],[-3.712,-2.075],[-3.712,-2.075],[-3.934,-3.523],[-6.094,-3.523],[-5.771,-1.424],[-4.868,0.424],[-3.995,1.465],[-3.344,2.027],[-2.35,2.676],[-0.934,3.243],[1.049,3.523],[3.031,3.243],[4.449,2.676],[5.441,2.027],[5.482,1.997],[5.615,1.895]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,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]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[26.201,40.411],"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 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-13.398,0],[0,-13.4],[13.398,0],[0,13.4]],"o":[[13.398,0],[0,13.4],[-13.398,0],[0,-13.4]],"v":[[0,-24.3],[24.3,0],[0,24.3],[-24.3,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[14.904,0],[0,-14.904],[-14.904,0],[0,14.904]],"o":[[-14.904,0],[0,14.904],[14.904,0],[0,-14.904]],"v":[[0,-27],[-27,0],[0,27],[27,0]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,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]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.25,27.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 5","np":4,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1200,"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 abbb4cb..63aa97f 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"het \'n prent gestuur"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Stoor tans skermkiekie..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Stoor tans skermskoot in werkprofiel …"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Stoor tans skermskoot in privaat"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Skermkiekie is gestoor"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Kon nie skermkiekie stoor nie"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Eksterne skerm"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Bind nuwe toestel saam"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik om nuwe toestel saam te bind"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Kon nie voorafstelling opdateer nie"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Deblokkeer toestelkamera en mikrofoon?"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 3bdf212..58940bb 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ምስል ተልኳል"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"ቅጽበታዊ ገፅ ዕይታ በማስቀመጥ ላይ..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"ቅጽበታዊ ገፅ እይታን ወደ የስራ መገለጫ በማስቀመጥ ላይ…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"ቅጽበታዊ ገጽ ዕይታን ወደ ግል በማስቀመጥ ላይ"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"ቅጽበታዊ ገፅ ዕይታ ተቀምጧል"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"ቅጽበታዊ ገፅ ዕይታን ማስቀመጥ አልተቻለም"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"ውጫዊ ማሳያ"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"የመሣሪያ ማይክሮፎን እገዳ ይነሳ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"የመሣሪያ ካሜራ እገዳ ይነሳ?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"የመሣሪያ ካሜራ እና ማይክሮፎን እገዳ ይነሳ?"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index ac210e2..b74b6cc 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"أرسَل صورة"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"جارٍ حفظ لقطة الشاشة..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"جارٍ حفظ لقطة الشاشة في ملف العمل…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"يتم حاليًا حفظ لقطة الشاشة في الملف الشخصي الخاص"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"تم حفظ لقطة الشاشة."</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"تعذّر حفظ لقطة الشاشة"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"الشاشة الخارجية"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"هل تريد إزالة حظر ميكروفون الجهاز؟"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"هل تريد إزالة حظر كاميرا الجهاز؟"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"هل تريد إزالة حظر الكاميرا والميكروفون؟"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 45363dd..e25a19e 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"এখন প্ৰতিচ্ছবি পঠিয়াইছে"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"স্ক্ৰীনশ্বট ছেভ কৰি থকা হৈছে…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"কৰ্মস্থানৰ প্ৰ’ফাইলত স্ক্ৰীনশ্বট ছেভ কৰি থকা হৈছে…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"ব্যক্তিগত প্ৰ’ফাইলত স্ক্ৰীনশ্বট ছেভ কৰি থকা হৈছে"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"স্ক্ৰীনশ্বট ছেভ কৰা হ’ল"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"স্ক্ৰীনশ্বট ছেভ কৰিব পৰা নগ\'ল"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"বাহ্যিক ডিছপ্লে’"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ডিভাইচৰ মাইক্ৰ\'ফ\'ন অৱৰোধৰ পৰা আঁতৰাবনে?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ডিভাইচৰ কেমেৰা অৱৰোধৰ পৰা আঁতৰাবনে?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ডিভাইচৰ কেমেৰা আৰু মাইক্ৰ\'ফ\'ন অৱৰোধৰ পৰা আঁতৰাবনে?"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index e796364..29fb86e 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"şəkil göndərdi"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Skrinşot yadda saxlanır..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"İş profili skrinşotu saxlanılır…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Skrinşot şəxsidə saxlanılır"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Skrinşot yadda saxlandı"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Skrinşotu yadda saxlamaq alınmadı"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Xarici displey"</string>
@@ -271,7 +272,7 @@
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Toxunaraq cihaza qoşulun, yaxud əlaqəni ayırın"</string>
     <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Yeni cihaz birləşdirin"</string>
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Hamısına baxın"</string>
-    <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth aç"</string>
+    <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth-u açın"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Qoşulub"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio paylaşma"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Yadda saxlandı"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Yeni cihaz birləşdirin"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yeni cihaz birləşdirmək üçün klikləyin"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Hazır ayar güncəllənmədi"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Cihaz kamerası və mikrofonu blokdan çıxarılsın?"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 23a09cad..f7c252e 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"je poslao/la sliku"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Čuvanje snimka ekrana..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Snimak ekrana se čuva na poslovnom profilu…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Snimak ekrana se čuva na privatnom profilu"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Snimak ekrana je sačuvan"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Čuvanje snimka ekrana nije uspelo"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Spoljni ekran"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Upari novi uređaj"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da biste uparili nov uređaj"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje zadatih podešavanja nije uspelo"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Želite da odblokirate kameru i mikrofon uređaja?"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 7dc9480..6008438 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"адпраўлены відарыс"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Захаванне скрыншота..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Захаванне здымка экрана ў працоўны профіль…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Здымак экрана захоўваецца ў прыватны профіль"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Здымак экрана захаваны"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Не атрымалася зрабіць здымак экрана"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Знешні дысплэй"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Разблакіраваць мікрафон прылады?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Разблакіраваць камеру прылады?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Разблакіраваць камеру і мікрафон прылады?"</string>
@@ -710,7 +713,7 @@
     <string name="notification_more_settings" msgid="4936228656989201793">"Дадатковыя налады"</string>
     <string name="notification_app_settings" msgid="8963648463858039377">"Наладзіць"</string>
     <string name="notification_conversation_bubble" msgid="2242180995373949022">"Паказаць усплывальнае апавяшчэнне"</string>
-    <string name="notification_conversation_unbubble" msgid="6908427185031099868">"Выдаліць усплывальныя апавяшчэнні"</string>
+    <string name="notification_conversation_unbubble" msgid="6908427185031099868">"Выдаліць усплывальныя чаты"</string>
     <string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="notification_menu_gear_description" msgid="6429668976593634862">"кіраванне апавяшчэннямі"</string>
     <string name="notification_menu_snooze_description" msgid="4740133348901973244">"параметры адкладвання апавяшчэнняў"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 42265b0..efaa31f 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"изпратено изображение"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Екранната снимка се запазва..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Екранната снимка се запазва в служебния профил…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Екранната снимка се запазва в личния потребителски профил"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Екранната снимка е запазена"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Не можа да се запази екранна снимка"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Външен екран"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Да се отблокира ли микрофонът на устройството?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Да се отблокира ли камерата на устройството?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Да се отблокират ли камерата и микрофонът на устройството?"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 3d008cb..6decb61 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"একটি ছবি পাঠানো হয়েছে"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"স্ক্রিনশট সেভ করা হচ্ছে..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"অফিস প্রোফাইলে স্ক্রিনশট সেভ করা হচ্ছে…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"ব্যক্তিগত প্রোফাইলে স্ক্রিনশট সেভ করা হয়েছে"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"স্ক্রিনশট সেভ করা হয়েছে"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"স্ক্রিনশট সেভ করা যায়নি"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"এক্সটার্নাল ডিসপ্লে"</string>
@@ -277,7 +278,7 @@
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"সেভ করা আছে"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ডিসকানেক্ট করুন"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"চালু করুন"</string>
-    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"আগামীকাল অটোমেটিক আবার চালু হবে"</string>
+    <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"আগামীকাল আবার অটোমেটিক চালু হবে"</string>
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"দ্রুত শেয়ার ও Find My Device-এর মতো ফিচার ব্লুটুথ ব্যবহার করে"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ব্লুটুথ আগামীকাল সকালে চালু হয়ে যাবে"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"অডিও শেয়ারিং"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ডিভাইসের মাইক্রোফোন আনব্লক করতে চান?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ডিভাইসের ক্যামেরা আনব্লক করতে চান?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ডিভাইসের ক্যামেরা এবং মাইক্রোফোন আনব্লক করতে চান?"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 96b83c1..1aea982 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"je poslao/la sliku"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Spašavanje snimka ekrana..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Pohranjivanje snimka ekrana na radni profil…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Pohranjivanje snimka ekrana na privatni profil"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Snimak ekrana je sačuvan"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Nije moguće sačuvati snimak ekrana"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Vanjski ekran"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uparite novi uređaj"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da uparite novi uređaj"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje zadane postavke nije uspjelo"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Deblokirati kameru i mikrofon uređaja?"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index cf9717f..8229e4b 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ha enviat una imatge"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"S\'està desant la captura de pantalla..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"S\'està desant la captura al perfil de treball…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"S\'està desant la captura de pantalla al perfil privat"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"S\'ha desat la captura de pantalla"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"No s\'ha pogut desar la captura de pantalla"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Pantalla externa"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Vincula un dispositiu nou"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fes clic per vincular un dispositiu nou"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"No s\'ha pogut actualitzar el valor predefinit"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vols desbloquejar la càmera i el micròfon del dispositiu?"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index a5a88d4..85cef4b6 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"odesílá obrázek"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Ukládání snímku obrazovky..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Ukládání snímku obrazovky do pracovního profilu…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Ukládání snímku obrazovky do soukromého profilu"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Snímek obrazovky byl uložen"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Snímek obrazovky se nepodařilo uložit"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Externí displej"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Spárovat nové zařízení"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknutím spárujete nové zařízení"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Předvolbu nelze aktualizovat"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Odblokovat fotoaparát a mikrofon zařízení?"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 55a5de9..836fe26 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sendte et billede"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Gemmer screenshot..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Gemmer screenshot på din arbejdsprofil…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Gemmer screenshottet på din private profil"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Screenshottet blev gemt"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Screenshottet kunne ikke gemmes"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Ekstern skærm"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Par ny enhed"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik for at parre en ny enhed"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Forindstillingen kunne ikke opdateres"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vil du fjerne blokeringen af enhedens kamera og mikrofon?"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 1f0550f..07a07a8 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"Bild gesendet"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Screenshot wird gespeichert..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Screenshot wird in Arbeitsprofil gespeichert…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Screenshot wird im vertraulichen Profil gespeichert"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Screenshot gespeichert"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Screenshot konnte nicht gespeichert werden"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Äußeres Display"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Neues Gerät koppeln"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klicken, um neues Gerät zu koppeln"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Voreinstellung konnte nicht aktualisiert werden"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Blockierung von Gerätekamera und Gerätemikrofon aufheben?"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 937ba70..a1dd5f9 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"έστειλε μια εικόνα"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Αποθήκευση στιγμιότυπου οθόνης..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Αποθήκευση στιγμιότ. οθόνης στο προφίλ εργασίας…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Αποθήκευση στιγμιότυπου οθόνης σε ιδιωτικό"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Το στιγμιότυπο οθόνης αποθηκεύτηκε"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Μη δυνατή αποθήκευση του στιγμιότυπου οθόνης"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Εξωτερική οθόνη"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Κατάργηση αποκλεισμού μικροφώνου συσκευής;"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Κατάργηση αποκλεισμού κάμερας συσκευής;"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Κατάργηση αποκλεισμού κάμερας και μικροφώνου συσκευής;"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index 137b243..542d660 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sent an image"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Saving screenshot…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Saving screenshot to work profile…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Saving screenshot to private"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Screenshot saved"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Couldn\'t save screenshot"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"External display"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 1f68f46..4ac0efe 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sent an image"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Saving screenshot…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Saving screenshot to work profile…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Saving screenshot to private"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Screenshot saved"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Couldn\'t save screenshot"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"External Display"</string>
@@ -371,6 +372,7 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string>
+    <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index 137b243..542d660 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sent an image"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Saving screenshot…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Saving screenshot to work profile…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Saving screenshot to private"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Screenshot saved"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Couldn\'t save screenshot"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"External display"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index 137b243..542d660 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sent an image"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Saving screenshot…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Saving screenshot to work profile…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Saving screenshot to private"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Screenshot saved"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Couldn\'t save screenshot"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"External display"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Unblock device camera and microphone?"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index eb705ae..67c332e 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‎‏‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‎‎‏‏‎‏‎‎‏‎‏‎‎‎‏‏‎sent an image‎‏‎‎‏‎"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‏‏‎‎‎‎‎‏‏‏‏‎‎‏‏‏‏‎‎‎‎‏‏‎‏‎‏‏‎‏‎‎‎‎‏‏‎‎‎‏‎‏‎‎‏‎‏‎Saving screenshot…‎‏‎‎‏‎"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‎‎‎‎‏‎‎‎‎‎‎‏‎‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‏‏‎‎‎‏‎‎‎‎‎‎‎Saving screenshot to work profile…‎‏‎‎‏‎"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‏‏‏‎‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‏‏‎‏‎‎‎‎‎‎‏‎‏‎‏‎‏‏‎‎‎‏‎Saving screenshot to private‎‏‎‎‏‎"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‏‏‎‎‏‏‏‎‏‏‏‏‎‎‏‏‎‎‎‏‏‏‏‏‏‎‏‏‎‎‎‎‎‎‎‏‏‎‏‎‏‏‏‎‎‏‎‎‎‏‎Screenshot saved‎‏‎‎‏‎"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‏‎‎‏‏‏‎‏‎‏‏‎‏‎‎‏‎‎‎‎‎‏‎‎‎‏‏‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‏‎‏‎‏‏‏‎‏‏‏‏‎‏‏‎Couldn\'t save screenshot‎‏‎‎‏‎"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‏‏‏‎‏‎‎‏‏‎‏‏‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‎‎‎‎‎‏‎‎‏‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‎‎‎External Display‎‏‎‎‏‎"</string>
@@ -371,6 +372,7 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‎‏‎‏‏‎‎‏‏‏‏‎‎‎‎‏‎‏‏‏‏‎‏‎‎‏‎‎‏‎‎‎‎‎‎‎‎‎‏‏‏‏‎‎‎‏‎‎‎‏‏‎‏‎‎Pair new device‎‏‎‎‏‎"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‎‎‎‎‏‎‎‏‏‎‎‏‎‎‏‏‎‎‎‏‏‏‎‏‎‎‎‏‎‏‏‏‏‎‏‎‎‏‏‏‎‎‏‏‎‏‎‏‎‏‎‏‎‎Click to pair new device‎‏‎‎‏‎"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‏‏‏‎‏‎‏‏‎‎‎‎‎‏‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‎‎‎‏‎‏‏‎‎‎‎Couldn\'t update preset‎‏‎‎‏‎"</string>
+    <string name="hearing_devices_preset_label" msgid="7878267405046232358">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‎‏‎‏‎‏‎‏‎‎‏‏‏‎‏‎‎‏‏‏‎‏‏‎‏‏‎‎‏‎‏‏‎‏‎‏‎‎‎‏‏‎‏‏‎‏‎‏‎‎‏‎‎‏‏‎‎Preset‎‏‎‎‏‎"</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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‎‎‏‎‎‏‎‎‏‎‏‏‏‎‏‎‎‏‎‎‏‏‎‏‏‏‏‎‎‎‎‎‎‎‏‏‏‎‏‏‏‎Unblock device camera and microphone?‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index ea7748c..c6d206b 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"envió una imagen"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Guardando la captura de pantalla..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Guardando cap. de pantalla en perfil de trabajo…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Guardando captura de pantalla en privado"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Se guardó la captura de pantalla"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"No se pudo guardar la captura de pantalla"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Pantalla externa"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Vincular dispositivo nuevo"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Haz clic para vincular un dispositivo nuevo"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"No se pudo actualizar el ajuste predeterminado"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"¿Quieres desbloquear la cámara y el micrófono del dispositivo?"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 8365067..76effd1 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ha enviado una imagen"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Guardando captura..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Guardando captura en el perfil de trabajo…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Guardando captura de pantalla en espacio privado"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Se ha guardado la captura de pantalla"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"No se ha podido guardar la captura de pantalla"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Pantalla externa"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Emparejar nuevo dispositivo"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Haz clic para emparejar un nuevo dispositivo"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"No se ha podido actualizar el preajuste"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"¿Desbloquear la cámara y el micrófono del dispositivo?"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 2df5681..3d15aab 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"saatis kujutise"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Kuvatõmmise salvestamine ..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Ekraanipildi salvestamine tööprofiilile …"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Ekraanipildi salvestamine privaatseks"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Ekraanipilt salvestati"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Ekraanipilti ei õnnestunud salvestada"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Väline ekraan"</string>
@@ -278,7 +279,7 @@
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"katkesta ühendus"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiveeri"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Lülita automaatselt homme uuesti sisse"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funktsioonid, nagu Kiirjagamine ja Leia mu seade, kasutavad Bluetoothi"</string>
+    <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Sellised funktsioonid nagu Kiirjagamine ja Leia mu seade kasutavad Bluetoothi."</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth lülitub sisse homme hommikul"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Heli jagamine"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Heli jagamine"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uue seadme sidumine"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Uue seadme sidumiseks klõpsake"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Eelseadistust ei saanud värskendada"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Kas tühistada seadme kaamera ja mikrofoni blokeerimine?"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 1f10821..db0e8e9 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"erabiltzaileak irudi bat bidali du"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Pantaila-argazkia gordetzen…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Pantaila-argazkia laneko profilean gordetzen…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Pantaila-argazkia profil pribatuan gordetzen"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Gorde da pantaila-argazkia"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Ezin izan da gorde pantaila-argazkia"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Kanpoko pantaila"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parekatu beste gailu bat"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Egin klik beste gailu bat parekatzeko"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ezin izan da eguneratu aurrezarpena"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Gailuaren kamera eta mikrofonoa desblokeatu nahi dituzu?"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 94befe4..f2372a6 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"تصویری ارسال کرد"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"درحال ذخیره نماگرفت…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"درحال ذخیره کردن نماگرفت در نمایه کاری…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"درحال ذخیره کردن نماگرفت در نمایه خصوصی"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"نماگرفت ذخیره شد"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"نماگرفت ذخیره نشد"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"نمایشگر خارجی"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"میکروفون دستگاه لغو انسداد شود؟"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"دوربین دستگاه لغو انسداد شود؟"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"دوربین و میکروفون دستگاه لغو انسداد شود؟"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 08c7adb..9172a9a 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"lähetti kuvan"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Tallennetaan kuvakaappausta..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Kuvakaappausta tallennetaan työprofiiliin…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Tallennetaan kuvakaappausta yksityiseen profiiliin"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Kuvakaappaus tallennettu"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Kuvakaappauksen tallennus epäonnistui"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Ulkoinen näyttö"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Muodosta uusi laitepari"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Muodosta uusi laitepari klikkaamalla"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Esiasetusta ei voitu muuttaa"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Kumotaanko laitteen kameran ja mikrofonin esto?"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 59b9e42..f2bae1b 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"a envoyé une image"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Enregistrement capture écran…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Sauv. de la capture dans le profil prof. en cours…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Enregistrement de la capture d\'écran dans le profil privé en cours…"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Capture d\'écran enregistrée"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Impossible d\'enregistrer la capture d\'écran"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Écran externe"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Associer un nouvel appareil"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Cliquez ici pour associer un nouvel appareil"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Impossible de mettre à jour le préréglage"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Débloquer l\'appareil photo et le microphone?"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 79f70af..e216c84 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"a envoyé une image"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Enregistrement de la capture d\'écran…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Enregistrement de capture d\'écran dans profil pro…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Enregistrement de la capture d\'écran dans le profil privé"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Capture d\'écran enregistrée"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Impossible d\'enregistrer la capture d\'écran"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Écran externe"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Associer un nouvel appareil"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Cliquer pour associer un nouvel appareil"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Impossible de mettre à jour les préréglages"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Débloquer l\'appareil photo et le micro de l\'appareil ?"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index fe2f112..1ccab5d 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -36,7 +36,7 @@
     <string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"Xirar pantalla automaticamente"</string>
     <string name="usb_device_permission_prompt" msgid="4414719028369181772">"Queres permitir que <xliff:g id="APPLICATION">%1$s</xliff:g> acceda a <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
     <string name="usb_device_permission_prompt_warn" msgid="2309129784984063656">"Queres permitir que a aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> acceda ao dispositivo (<xliff:g id="USB_DEVICE">%2$s</xliff:g>)?\nEsta aplicación non está autorizada para realizar gravacións, pero podería capturar audio a través deste dispositivo USB."</string>
-    <string name="usb_audio_device_permission_prompt_title" msgid="4221351137250093451">"Permitir que <xliff:g id="APPLICATION">%1$s</xliff:g> acceda a <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+    <string name="usb_audio_device_permission_prompt_title" msgid="4221351137250093451">"Queres permitir que a aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> acceda a <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
     <string name="usb_audio_device_confirm_prompt_title" msgid="8828406516732985696">"Abrir <xliff:g id="APPLICATION">%1$s</xliff:g> para usar <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
     <string name="usb_audio_device_prompt_warn" msgid="2504972133361130335">"Esta aplicación non está autorizada a realizar gravacións, pero podería capturar audio a través deste dispositivo USB. Ao usar a aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> con este dispositivo, é posible que non se escoiten chamadas, notificacións nin alarmas."</string>
     <string name="usb_audio_device_prompt" msgid="7944987408206252949">"Ao usar a aplicación <xliff:g id="APPLICATION">%1$s</xliff:g> con este dispositivo, é posible que non se escoiten chamadas, notificacións nin alarmas."</string>
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"enviou unha imaxe"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Gardando captura de pantalla…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Gardando captura de pantalla no perfil de traballo"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Gardando captura de pantalla no perfil privado"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Gardouse a captura de pantalla"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Non se puido gardar a captura de pantalla"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Pantalla externa"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Vincular dispositivo novo"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fai clic para vincular un novo dispositivo"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Non se puido actualizar a configuración predeterminada"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Queres desbloquear a cámara e o micrófono do dispositivo?"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 299598f..e096fff 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"છબી મોકલી"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"સ્ક્રીનશોટ સાચવી રહ્યું છે…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"ઑફિસની પ્રોફાઇલમાં સ્ક્રીનશૉટ સાચવી રહ્યાં છીએ…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"ખાનગી પ્રોફાઇલમાં સ્ક્રીનશૉટ સાચવી રહ્યાં છીએ"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"સ્ક્રીનશૉટ સાચવ્યો"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"સ્ક્રીનશૉટ સાચવી શક્યાં નથી"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"બાહ્ય ડિસ્પ્લે"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ડિવાઇસના માઇક્રોફોનને અનબ્લૉક કરીએ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ડિવાઇસના કૅમેરાને અનબ્લૉક કરીએ?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ડિવાઇસના કૅમેરા અને માઇક્રોફોનને અનબ્લૉક કરીએ?"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 4c34814..d8c7dcd 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"एक इमेज भेजी गई"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"स्क्रीनशॉट सहेजा जा रहा है..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"स्क्रीनशॉट, वर्क प्रोफ़ाइल में सेव किया जा रहा है…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"स्क्रीनशॉट को निजी प्रोफ़ाइल में सेव किया जा रहा है"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"स्क्रीनशॉट सेव किया गया"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"स्क्रीनशॉट सेव नहीं किया जा सका"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"बाहरी डिसप्ले"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"क्या आपको डिवाइस का माइक्रोफ़ोन अनब्लॉक करना है?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"क्या आपको डिवाइस का कैमरा अनब्लॉक करना है?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"क्या आप डिवाइस का कैमरा और माइक्रोफ़ोन अनब्लॉक करना चाहते हैं?"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 5b5bddc..19625bd 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"šalje sliku"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Spremanje snimke zaslona..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Spremanje snimke zaslona na poslovni profil…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Spremanje snimke zaslona na osobni profil"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Snimka zaslona je spremljena"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Snimka zaslona nije spremljena"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Vanjski prikaz"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uparite novi uređaj"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da biste uparili novi uređaj"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje unaprijed definiranih postavki nije uspjelo"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Želite li deblokirati kameru i mikrofon uređaja?"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index a2cf7b2..7099033 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"képet küldött"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Képernyőkép mentése..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Képernyőkép mentése a munkaprofilba…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Képernyőkép mentése a privát területre"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"A képernyőkép mentése sikerült"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Nem sikerült a képernyőkép mentése"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Külső kijelző"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Új eszköz párosítása"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kattintson új eszköz párosításához"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nem sikerült frissíteni a beállításkészletet"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Feloldja az eszköz kamerájának és mikrofonjának letiltását?"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index b0f4848..c539407 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"պատկեր է ուղարկվել"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Սքրինշոթը պահվում է..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Սքրինշոթը պահվում է աշխատանքային պրոֆիլում…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Սքրինշոթը պահվում է մասնավոր պրոֆիլում"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Սքրինշոթը պահվեց"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Չհաջողվեց պահել սքրինշոթը"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Արտաքին էկրան"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Արգելահանե՞լ սարքի խոսափողը"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Արգելահանե՞լ սարքի տեսախցիկը"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Արգելահանե՞լ սարքի տեսախցիկը և խոսափողը"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index a0795dc..5af06e8 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"mengirim gambar"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Menyimpan screenshot..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Menyimpan screenshot ke profil kerja …"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Menyimpan screenshot ke profil privasi"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Screenshot disimpan"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Tidak dapat menyimpan screenshot"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Layar Eksternal"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sambungkan perangkat baru"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik untuk menyambungkan perangkat baru"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Tidak dapat memperbarui preset"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Berhenti memblokir kamera dan mikrofon perangkat?"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index f32f6a6..ee99882 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"sendi mynd"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Vistar skjámynd…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Vistar skjámynd á vinnusnið…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Vistar skjámynd í lokað"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Skjámynd vistuð"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Ekki var hægt að vista skjámynd"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Ytri skjár"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Para nýtt tæki"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Smelltu til að para nýtt tæki"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Tókst ekki að uppfæra forstillingu"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Opna fyrir myndavél og hljóðnema tækisins?"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 1988459..b42bd95 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -76,6 +76,8 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"è stata inviata un\'immagine"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Salvataggio screenshot…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Salvataggio screenshot nel profilo di lavoro…"</string>
+    <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) -->
+    <skip />
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Screenshot salvato"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Impossibile salvare lo screenshot"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Display esterno"</string>
@@ -371,6 +373,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Accoppia nuovo dispositivo"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fai clic per accoppiare un nuovo dispositivo"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Impossibile aggiornare preset"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vuoi sbloccare la fotocamera e il microfono del dispositivo?"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index d31a95d..71d0c6b 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"נשלחה תמונה"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"המערכת שומרת את צילום המסך..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"צילום המסך נשמר בפרופיל העבודה…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"צילום המסך נשמר בפרופיל האישי"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"צילום המסך נשמר"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"לא ניתן היה לשמור את צילום המסך"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"תצוגה במסך חיצוני"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"לבטל את חסימת המיקרופון של המכשיר?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"לבטל את חסימת המצלמה של המכשיר?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"לבטל את חסימת המצלמה והמיקרופון של המכשיר?"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index bff8487..d6dc93c 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"画像を送信しました"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"スクリーンショットを保存しています..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"スクリーンショットを仕事用プロファイルに保存中…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"スクリーンショットをプライベートに保存しています"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"スクリーンショットを保存しました"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"スクリーンショット保存エラー"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"外側ディスプレイ"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"デバイスのマイクのブロックを解除しますか?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"デバイスのカメラのブロックを解除しますか?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"デバイスのカメラとマイクのブロックを解除しますか?"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 51ba080..2670e45 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"გაიგზავნა სურათი"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"ეკრანის სურათის შენახვა…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"მიმდინარეობს ეკრანის ანაბეჭდის შენახვა სამუშაო პროფილში…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"მიმდინარეობს ეკრანის ანაბეჭდის შენახვა კერძო სივრცეში"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"ეკრანის ანაბეჭდი შენახულია"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"ეკრანის ანაბეჭდის შენახვა ვერ მოხერხდა"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"გარე ეკრანი"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"გსურთ მოწყობილობის მიკროფონის განბლოკვა?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"გსურთ მოწყობილობის კამერის განბლოკვა?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"გსურთ მოწყობილობის კამერის და მიკროფონის განბლოკვა?"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 27c8512..c709bbe 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"сурет жіберілді"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Скриншотты сақтауда…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Скриншот жұмыс профиліне сақталып жатыр…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Скриншот жеке профильде сақталып жатыр."</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Скриншот сақталды"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Скриншот сақталмады"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Сыртқы дисплей"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Құрылғы микрофонын блоктан шығару керек пе?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Құрылғы камерасын блоктан шығару керек пе?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Құрылғы камерасы мен микрофонын блоктан шығару керек пе?"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index bd0bf98..042e748 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"បាន​ផ្ញើរូបភាព"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"កំពុង​រក្សាទុក​រូបថត​អេក្រង់..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"កំពុងរក្សាទុករូបថតអេក្រង់ទៅកម្រងព័ត៌មានការងារ…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"កំពុងរក្សាទុករូបថត​អេក្រង់ទៅក្នុងកម្រងព័ត៌មានឯកជន"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"បានរក្សាទុក​រូបថតអេក្រង់"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"មិន​អាច​រក្សាទុក​រូបថត​អេក្រង់បានទេ"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"ផ្ទាំង​អេក្រង់​ខាង​ក្រៅ"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ឈប់ទប់ស្កាត់​មីក្រូហ្វូន​របស់ឧបករណ៍ឬ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ឈប់ទប់ស្កាត់​កាមេរ៉ា​របស់ឧបករណ៍ឬ?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ឈប់ទប់ស្កាត់​កាមេរ៉ា និងមីក្រូហ្វូន​របស់ឧបករណ៍ឬ?"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index bc7ab50..8b1ab5f 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ಚಿತ್ರವನ್ನು ಕಳುಹಿಸಲಾಗಿದೆ"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್ ಉಳಿಸಲಾಗುತ್ತಿದೆ…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್‌ಗೆ ಸ್ಕ್ರೀನ್‌ಶಾಟ್‌ ಉಳಿಸಲಾಗುತ್ತಿದೆ…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್‌ ಅನ್ನು ಖಾಸಗಿ ಪ್ರೊಫೈಲ್‌ಗೆ ಸೇವ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್‌ ಅನ್ನು ಸೇವ್ ಮಾಡಲಾಗಿದೆ"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"ಸ್ಕ್ರೀನ್‌ಶಾಟ್ ಅನ್ನು ಉಳಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"ಬಾಹ್ಯ ಡಿಸ್‌ಪ್ಲೇ"</string>
@@ -279,7 +280,7 @@
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ಸಕ್ರಿಯಗೊಳಿಸಿ"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ನಾಳೆ ಪುನಃ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಆನ್ ಮಾಡಿ"</string>
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ಕ್ವಿಕ್ ಶೇರ್ ಮತ್ತು Find My Device ನಂತಹ ಫೀಚರ್‌ಗಳು ಬ್ಲೂಟೂತ್ ಅನ್ನು ಬಳಸುತ್ತವೆ"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ಬ್ಲೂಟೂತ್ ನಾಳೆ ಬೆಳಿಗ್ಗೆ ಆನ್ ಆಗುತ್ತದೆ"</string>
+    <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ಬ್ಲೂಟೂತ್ ನಾಳೆ ಬೆಳಗ್ಗೆ ಆನ್ ಆಗುತ್ತದೆ"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ಆಡಿಯೋ ಹಂಚಿಕೊಳ್ಳುವಿಕೆ"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ಆಡಿಯೋವನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ಬ್ಯಾಟರಿ"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ಸಾಧನದ ಮೈಕ್ರೋಫೋನ್ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆಯಬೇಕೆ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ಸಾಧನದ ಕ್ಯಾಮರಾ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆಯಬೇಕೆ?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ಸಾಧನದ ಕ್ಯಾಮರಾ ಮತ್ತು ಮೈಕ್ರೋಫೋನ್ ಅನ್ನು ಅನ್‍ಬ್ಲಾಕ್ ಮಾಡಬೇಕೇ?"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 0e6f9bf..8d34d00 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"이미지 보냄"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"캡쳐화면 저장 중..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"직장 프로필에 스크린샷 저장 중…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"스크린샷을 비공개 프로필에 저장 중"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"스크린샷 저장됨"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"스크린샷을 저장할 수 없음"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"외부 디스플레이"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"기기 마이크를 차단 해제하시겠습니까?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"기기 카메라를 차단 해제하시겠습니까?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"기기 카메라 및 마이크를 차단 해제하시겠습니까?"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 383a8e1..975faf1 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"сүрөт жөнөттү"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Скриншот сакталууда..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Скриншот жумуш профилине сакталууда…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Скриншот жеке профилге сакталууда"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Скриншот сакталды"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Скриншот сакталган жок"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Тышкы экран"</string>
@@ -278,7 +279,7 @@
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ажыратуу"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"иштетүү"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Эртең автоматтык түрдө кайра күйгүзүү"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Тез бөлүшүү жана Түзмөгүм кайда? сыяктуу функциялар Bluetooth\'ту колдонушат"</string>
+    <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth Тез бөлүшүү жана Түзмөгүм кайда? сыяктуу функцияларда колдонулат"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth эртең таңда күйөт"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Чогуу угуу"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Чогуу угулууда"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Түзмөктүн микрофонун бөгөттөн чыгарасызбы?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Түзмөктүн камерасын бөгөттөн чыгарасызбы?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Түзмөктүн камерасы менен микрофону бөгөттөн чыгарылсынбы?"</string>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 56ebc06..aea79e8 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -28,9 +28,7 @@
 
     <!--  In landscape the security footer is actually part of the header,
     and needs to be as short as the header  -->
-    <dimen name="qs_security_footer_single_line_height">@*android:dimen/quick_qs_offset_height</dimen>
     <dimen name="qs_footer_padding">14dp</dimen>
-    <dimen name="qs_security_footer_background_inset">12dp</dimen>
 
     <dimen name="volume_tool_tip_top_margin">12dp</dimen>
     <dimen name="volume_row_slider_height">128dp</dimen>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index a136976..22a64f8 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ສົ່ງຮູບແລ້ວ"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"ກຳລັງບັນທຶກພາບໜ້າຈໍ..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"ກຳລັງບັນທຶກຮູບໜ້າຈໍໃສ່ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"ກຳລັງບັນທຶກຮູບໜ້າຈໍໄວ້ໃນໂປຣໄຟລ໌ສ່ວນຕົວ"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"ບັນທຶກຮູບໜ້າຈໍໄວ້ແລ້ວ"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"ບໍ່ສາມາດບັນທຶກຮູບໜ້າຈໍໄດ້"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"ຈໍສະແດງຜົນພາຍນອກ"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ປົດບລັອກໄມໂຄຣໂຟນອຸປະກອນບໍ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ປົດບລັອກກ້ອງຖ່າຍຮູບອຸ​ປະ​ກອນບໍ?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ຍົກເລີກການບລັອກກ້ອງຖ່າຍຮູບ ຫຼື ໄມໂຄຣໂຟນອຸ​ປະ​ກອນບໍ?"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 96be8e2..bec3cc1 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"išsiuntė vaizdą"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Išsaugoma ekrano kopija..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Išsaugoma ekrano kopija darbo profilyje…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Ekrano kopija išsaugoma privačiame profilyje"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Ekrano kopija išsaugota"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Ekrano kopijos išsaugoti nepavyko"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Išorinė pateiktis"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Susieti naują įrenginį"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Spustelėkite, kad susietumėte naują įrenginį"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Išankstinių nustatymų atnaujinti nepavyko"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Panaikinti įrenginio fotoaparato ir mikrofono blokavimą?"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index b8a5476..001f6a0 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"nosūtīts attēls"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Notiek ekrānuzņēmuma saglabāšana..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Notiek ekrānuzņēmuma saglabāšana darba profilā…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Ekrānuzņēmums tiek saglabāts privātajā profilā"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Ekrānuzņēmums saglabāts"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Ekrānuzņēmumu neizdevās saglabāt."</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Ārējais displejs"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Savienot pārī jaunu ierīci"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Noklikšķiniet, lai savienotu pārī jaunu ierīci"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nevarēja atjaunināt pirmsiestatījumu"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vai atbloķēt ierīces kameru un mikrofonu?"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 2e0edbe..12bf568 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"испрати слика"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Сликата на екранот се зачувува..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Се зачувува слика од екранот на вашиот работен профил…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Сликата од екранот се зачувува во приватниот профил"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Сликата од екранот е зачувана"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Не може да се зачува слика од екранот"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Надворешен екран"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Да се одблокира пристапот до микрофонот на уредот?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Да се одблокира пристапот до камерата на уредот?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Да се одблокира пристапот до камерата и микрофонот на уредот?"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index ad7d723..d165242 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -26,7 +26,7 @@
     <string name="battery_low_percent_format" msgid="4276661262843170964">"<xliff:g id="PERCENTAGE">%s</xliff:g> ശേഷിക്കുന്നു"</string>
     <string name="invalid_charger_title" msgid="938685362320735167">"USB വഴി ചാർജ് ചെയ്യാനാകില്ല"</string>
     <string name="invalid_charger_text" msgid="2339310107232691577">"ഉപകരണത്തിനൊപ്പം ലഭിച്ച ചാർജർ ഉപയോഗിക്കുക"</string>
-    <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"ബാറ്ററി ലാഭിക്കൽ ഓണാക്കണോ?"</string>
+    <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"ബാറ്ററി സേവർ ഓണാക്കണോ?"</string>
     <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"ബാറ്ററി ലാഭിക്കലിനെ കുറിച്ച്"</string>
     <string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"ഓൺ ചെയ്യുക"</string>
     <string name="battery_saver_start_action" msgid="8353766979886287140">"ഓണാക്കുക"</string>
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ചിത്രം അയച്ചു"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"സ്‌ക്രീൻഷോട്ട് സംരക്ഷിക്കുന്നു..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"ഔദ്യോഗിക പ്രൊഫൈലിലേക്ക് സ്ക്രീൻഷോട്ട് സംരക്ഷിക്കുന്നു…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"സ്ക്രീൻഷോട്ട് സ്വകാര്യമാക്കി സംരക്ഷിക്കുന്നു"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"സ്‌ക്രീൻഷോട്ട് സംരക്ഷിച്ചു"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"സ്‌ക്രീൻഷോട്ട് സംരക്ഷിക്കാനായില്ല"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"ബാഹ്യ ഡിസ്പ്ലേ"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ഉപകരണ മൈക്രോഫോൺ അൺബ്ലോക്ക് ചെയ്യണോ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ഉപകരണ ക്യാമറ അൺബ്ലോക്ക് ചെയ്യണോ?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ഉപകരണ ക്യാമറയോ മൈക്രോഫോണോ അൺബ്ലോക്ക് ചെയ്യണോ?"</string>
@@ -720,7 +723,7 @@
     <string name="snoozed_for_time" msgid="7586689374860469469">"<xliff:g id="TIME_AMOUNT">%1$s</xliff:g> സമയത്തേക്ക് സ്‌നൂസ് ‌ചെയ്‌തു"</string>
     <string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# മണിക്കൂർ}=2{# മണിക്കൂർ}other{# മണിക്കൂർ}}"</string>
     <string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# മിനിറ്റ്}other{# മിനിറ്റ്}}"</string>
-    <string name="battery_detail_switch_title" msgid="6940976502957380405">"ബാറ്ററി ലാഭിക്കൽ"</string>
+    <string name="battery_detail_switch_title" msgid="6940976502957380405">"ബാറ്ററി സേവർ"</string>
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"ബട്ടൺ <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"ഹോം"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"ബാക്ക്"</string>
@@ -943,7 +946,7 @@
     <string name="slice_permission_checkbox" msgid="4242888137592298523">"ഏത് ആപ്പിൽ നിന്നും സ്ലൈസുകൾ കാണിക്കാൻ <xliff:g id="APP">%1$s</xliff:g>-നെ അനുവദിക്കുക"</string>
     <string name="slice_permission_allow" msgid="6340449521277951123">"അനുവദിക്കുക"</string>
     <string name="slice_permission_deny" msgid="6870256451658176895">"നിരസിക്കുക"</string>
-    <string name="auto_saver_title" msgid="6873691178754086596">"ബാറ്ററി ലാഭിക്കൽ ഷെഡ്യൂൾ ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
+    <string name="auto_saver_title" msgid="6873691178754086596">"ബാറ്ററി സേവർ ഷെഡ്യൂൾ ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
     <string name="auto_saver_text" msgid="3214960308353838764">"ബാറ്ററി ചാർജ് തീരാൻ സാധ്യതയുണ്ടെങ്കിൽ ഓണാക്കുക"</string>
     <string name="no_auto_saver_action" msgid="7467924389609773835">"വേണ്ട"</string>
     <string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"ഉപയോഗത്തിലാണ്"</string>
@@ -1017,7 +1020,7 @@
     <string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"എഡ്‌ജിലേക്ക് നീക്കി മറയ്‌ക്കുക"</string>
     <string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"എഡ്‌ജിൽ നിന്ന് നീക്കി കാണിക്കൂ"</string>
     <string name="accessibility_floating_button_action_remove_menu" msgid="6730432848162552135">"നീക്കം ചെയ്യുക"</string>
-    <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"മാറ്റുക"</string>
+    <string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"ടോഗിൾ ചെയ്യുക"</string>
     <string name="accessibility_floating_button_action_edit" msgid="1688227814600463987">"എഡിറ്റ് ചെയ്യുക"</string>
     <string name="quick_controls_title" msgid="6839108006171302273">"ഉപകരണ നിയന്ത്രണങ്ങൾ"</string>
     <string name="controls_providers_title" msgid="6879775889857085056">"നിയന്ത്രണങ്ങൾ ചേർക്കാൻ ആപ്പ് തിരഞ്ഞെടുക്കുക"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index fb3531f..2a0b70a 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"зураг илгээсэн"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Дэлгэцийн агшинг хадгалж байна…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Дэлгэцийн агшныг ажлын профайлд хадгалж байна…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Дэлгэцийн агшныг хаалттай профайлд хадгалж байна"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Дэлгэцээс дарсан зургийг хадгалсан"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Дэлгэцээс дарсан зургийг хадгалж чадсангүй"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Гадаад дэлгэц"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Төхөөрөмжийн микрофоныг блокоос гаргах уу?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Төхөөрөмжийн камерыг блокоос гаргах уу?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Төхөөрөмжийн камер болон микрофоныг блокоос гаргах уу?"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 249e7ac..017a6c8 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"इमेज पाठवली आहे"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"स्क्रीनशॉट सेव्ह करत आहे…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"कार्य प्रोफाइलवर स्क्रीनशॉट सेव्ह करत आहे…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"स्क्रीनशॉट खाजगीमध्ये सेव्ह करत आहे"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"स्क्रीनशॉट सेव्ह केला"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"स्क्रीनशॉट सेव्ह करू शकलो नाही"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"बाह्य डिस्प्ले"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"डिव्हाइसचा मायक्रोफोन अनब्लॉक करायचा आहे का?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"डिव्हाइसचा कॅमेरा अनब्लॉक करायचा आहे का?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"डिव्हाइसचा कॅमेरा आणि मायक्रोफोन अनब्लॉक करायचा आहे का?"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index e21daa2..23e4559 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"menghantar imej"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Menyimpan tangkapan skrin..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Menyimpan tangkapan skrin ke profil kerja…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Menyimpan tangkapan skrin pada profil peribadi"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Tangkapan skrin disimpan"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Tidak dapat menyimpan tangkapan skrin"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Paparan Luaran"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Gandingkan peranti baharu"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik untuk menggandingkan peranti baharu"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Tidak dapat mengemaskinikan pratetapan"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Nyahsekat kamera dan mikrofon peranti?"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 000d7d3..c3c4205 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ပုံပို့ထားသည်"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"ဖန်သားပြင်ဓါတ်ပုံရိုက်ခြင်းအား သိမ်းဆည်းပါမည်"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"အလုပ်ပရိုဖိုင်တွင် ဖန်သားပြင်ဓာတ်ပုံ သိမ်းနေသည်…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"ဖန်သားပြင်ဓာတ်ပုံကို သီးသန့်ပရိုဖိုင်တွင် သိမ်းနေသည်"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"ဖန်သားပြင်ဓာတ်ပုံကို သိမ်းပြီးပါပြီ"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"မျက်နှာပြင်ပုံကို သိမ်း၍မရပါ"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"ပြင်ပဖန်သားပြင်"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"စက်၏မိုက်ခရိုဖုန်းကို ပြန်ဖွင့်မလား။"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"စက်၏ကင်မရာကို ပြန်ဖွင့်မလား။"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"စက်၏ကင်မရာနှင့် မိုက်ခရိုဖုန်းကို ပြန်ဖွင့်မလား။"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 3fdb8f1..dbcfaa3 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"har sendt et bilde"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Lagrer skjermdumpen …"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Lagrer skjermdumpen i jobbprofilen …"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Lagrer skjermdump i den private profilen"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Skjermdumpen er lagret"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Kunne ikke lagre skjermdump"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Ekstern skjerm"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Koble til en ny enhet"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klikk for å koble til en ny enhet"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Kunne ikke oppdatere forhåndsinnstillingen"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vil du oppheve blokkeringen av enhetskameraet og -mikrofonen?"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index fe4b412..aa7e314 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"कुनै छवि पठाइयो"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"स्क्रिनसट बचत गर्दै…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"कार्य प्रोफाइलमा स्क्रिनसट सेभ गरिँदै छ…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"निजी प्रोफाइलमा स्क्रिनसट सेभ गरिँदै छ"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"स्क्रिनसट सेभ गरियो"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"स्क्रिनसट सुरक्षित गर्न सकिएन"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"बाह्य डिस्प्ले"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"डिभाइसको माइक्रोफोन अनब्लक गर्ने हो?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"डिभाइसको क्यामेरा अनब्लक गर्ने हो?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"डिभाइसको क्यामेरा र माइक्रोफोन अनब्लक गर्ने हो?"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index add74db..3b460e5 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"heeft een afbeelding gestuurd"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Screenshot opslaan..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Screenshot opslaan in werkprofiel…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Screenshot opslaan in privéprofiel"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Screenshot opgeslagen"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Kan screenshot niet opslaan"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Extern scherm"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Nieuw apparaat koppelen"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik om nieuw apparaat te koppelen"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Kan voorinstelling niet updaten"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Blokkeren van apparaatcamera en -microfoon opheffen?"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 6a93557..29a5e73 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ଏକ ଛବି ପଠାଯାଇଛି"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"ସ୍କ୍ରୀନଶଟ୍‍ ସେଭ୍‍ କରାଯାଉଛି…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"ୱାର୍କ ପ୍ରୋଫାଇଲରେ ସ୍କ୍ରିନସଟ ସେଭ କରାଯାଉଛି…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"ପ୍ରାଇଭେଟରେ ସ୍କ୍ରିନସଟକୁ ସେଭ କରାଯାଉଛି"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"ସ୍କ୍ରୀନଶଟ୍ ସେଭ୍ ହୋଇଛି"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"ସ୍କ୍ରୀନ୍‍ଶଟ୍ ସେଭ୍ କରିହେବ ନାହିଁ"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"ଏକ୍ସଟର୍ନଲ ଡିସପ୍ଲେ"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ଡିଭାଇସର ମାଇକ୍ରୋଫୋନକୁ ଅନବ୍ଲକ କରିବେ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ଡିଭାଇସର କେମେରାକୁ ଅନବ୍ଲକ କରିବେ?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ଡିଭାଇସର କ୍ୟାମେରା ଏବଂ ମାଇକ୍ରୋଫୋନକୁ ଅନବ୍ଲକ୍ କରିବେ?"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index c908618..6245575 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ਚਿੱਤਰ ਭੇਜਿਆ ਗਿਆ"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਸੁਰੱਖਿਅਤ ਕਰ ਰਿਹਾ ਹੈ…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ \'ਤੇ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਨੂੰ ਪ੍ਰਾਈਵੇਟ ਵਜੋਂ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"ਬਾਹਰੀ ਡਿਸਪਲੇ"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ਕੀ ਡੀਵਾਈਸ ਦੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੂੰ ਅਣਬਲਾਕ ਕਰਨਾ ਹੈ?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ਕੀ ਡੀਵਾਈਸ ਦੇ ਕੈਮਰੇ ਨੂੰ ਅਣਬਲਾਕ ਕਰਨਾ ਹੈ?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ਕੀ ਡੀਵਾਈਸ ਦੇ ਕੈਮਰੇ ਅਤੇ ਮਾਈਕ੍ਰੋਫ਼ੋਨ ਨੂੰ ਅਣਬਲਾਕ ਕਰਨਾ ਹੈ?"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index a13a1d6..e8e6f09 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"wysłano obraz"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Zapisywanie zrzutu ekranu..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Zapisuję zrzut ekranu w profilu służbowym…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Zapisuję zrzut ekranu w profilu prywatnym"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Zrzut ekranu został zapisany"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Nie udało się zapisać zrzutu ekranu"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Wyświetlacz zewnętrzny"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sparuj nowe urządzenie"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknij, aby sparować nowe urządzenie"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nie udało się zaktualizować gotowego ustawienia"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Odblokować aparat i mikrofon urządzenia?"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index da72fd5..e72b45a 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"enviou uma imagem"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Salvando captura de tela..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Salvando captura de tela no perfil de trabalho…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Salvando captura de tela no perfil particular"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Captura de tela salva"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Falha ao salvar a captura de tela"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Tela externa"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parear novo dispositivo"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para parear o novo dispositivo"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Não foi possível atualizar a predefinição"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Desbloquear a câmera e o microfone do dispositivo?"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 1d9a8da..1912b7e 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"enviou uma imagem"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"A guardar captura de ecrã..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"A guardar captura de ecrã no perfil de trabalho…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"A guardar a captura de ecrã em privado"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Captura de ecrã guardada"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Não foi possível guardar a captura de ecrã"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Ecrã externo"</string>
@@ -278,7 +279,7 @@
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desassociar"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Reativar amanhã automaticamente"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"As funcionalidades como a Partilha rápida e o serviço Localizar o meu dispositivo usam o Bluetooth"</string>
+    <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funcionalidades como a Partilha rápida e o serviço Localizar o meu dispositivo usam o Bluetooth"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth vai ser ativado amanhã de manhã"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Partilha de áudio"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"A partilhar áudio"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sincronizar novo dispositivo"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para sincronizar um novo dispositivo"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Não foi possível atualizar a predefinição"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Quer desbloquear a câmara e o microfone?"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index da72fd5..e72b45a 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"enviou uma imagem"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Salvando captura de tela..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Salvando captura de tela no perfil de trabalho…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Salvando captura de tela no perfil particular"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Captura de tela salva"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Falha ao salvar a captura de tela"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Tela externa"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parear novo dispositivo"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para parear o novo dispositivo"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Não foi possível atualizar a predefinição"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Desbloquear a câmera e o microfone do dispositivo?"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index f0f5987..b6614c6 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"a trimis o imagine"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Se salvează captura de ecran..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Se salvează captura în profilul de serviciu…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Se salvează captura de ecran în profilul privat"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Captură de ecran salvată"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Nu s-a putut salva captura de ecran"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Afișaj extern"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Asociază un nou dispozitiv"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Dă clic pentru a asocia un nou dispozitiv"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nu s-a putut actualiza presetarea"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Deblochezi camera și microfonul dispozitivului?"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index bb79bc2..d6fa6ab 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"отправлено изображение"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Сохранение..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Сохранение скриншота в рабочем профиле…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Сохранение скриншота в частный профиль…"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Скриншот сохранен"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Не удалось сохранить скриншот"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Внешний дисплей"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Разблокировать микрофон устройства?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Разблокировать камеру устройства?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Разблокировать камеру и микрофон устройства?"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index ad801c2..3de4225 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"රූපයක් එවන ලදී"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"තිර රුව සුරැකෙමින් පවතී…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"කාර්යාල පැතිකඩ වෙත තිර රුව සුරකිමින්…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"තිර රුව පුද්ගලික ලෙස සුරැකේ"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"තිර රුව සුරකින ලදී"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"තිර රුව සුරැකිය නොහැකි විය"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"බාහිර සංදර්ශකය"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"උපාංග මයික්‍රෆෝනය අවහිර කිරීම ඉවත් කරන්නද?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"උපාංග කැමරාව අවහිර කිරීම ඉවත් කරන්නද?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"උපාංග කැමරාව සහ මයික්‍රෆෝනය අවහිර කිරීම ඉවත් කරන්නද?"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 5631ab0..cb8cf5b 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"odoslal(a) obrázok"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Prebieha ukladanie snímky obrazovky..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Ukladá sa snímka obrazovky do pracovného profilu…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Snímka obrazovky sa ukladá do súkromného profilu"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Snímka obrazovky bola uložená"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Snímku obrazovky sa nepodarilo uložiť"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Externá obrazovka"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Spárovať nové zariadenie"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknutím spárujete nové zariadenie"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Predvoľbu sa nepodarilo aktualizovať"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Chcete odblokovať fotoaparát a mikrofón zariadenia?"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index a6b31c9..a3e4487 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"je poslal(-a) sliko"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Shranjevanje posnetka zaslona ..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Shranjevanje posnetka zaslona v delovni profil …"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Shranjevanje posnetka zaslona v zasebni profil"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Posnetek zaslona je shranjen"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Posnetka zaslona ni bilo mogoče shraniti"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Zunanji zaslon"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Seznanitev nove naprave"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite za seznanitev nove naprave"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Prednastavljenih vrednosti ni bilo mogoče posodobiti"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Želite odblokirati fotoaparat in mikrofon v napravi?"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index acbf234..b06890d 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -76,6 +76,8 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"dërgoi një imazh"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Po ruan pamjen e ekranit…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Pamja e ekranit po ruhet te profili i punës…"</string>
+    <!-- no translation found for screenshot_saving_private_profile (8934706048497093297) -->
+    <skip />
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Pamja e ekranit u ruajt"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Pamja e ekranit nuk mund të ruhej"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Ekrani i jashtëm"</string>
@@ -371,6 +373,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Çifto pajisje të re"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliko për të çiftuar një pajisje të re"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Paravendosja nuk mund të përditësohej"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Të zhbllokohen kamera dhe mikrofoni i pajisjes?"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index d21536b..2b851b4 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"је послао/ла слику"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Чување снимка екрана..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Снимак екрана се чува на пословном профилу…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Снимак екрана се чува на приватном профилу"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Снимак екрана је сачуван"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Чување снимка екрана није успело"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Спољни екран"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Желите да одблокирате микрофон уређаја?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Желите да одблокирате камеру уређаја?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Желите да одблокирате камеру и микрофон уређаја?"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 95c5e15..0d1614f 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"har skickat en bild"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Skärmbilden sparas ..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Sparar skärmbild i jobbprofilen …"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Sparar skärmbilden till privat profil"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Skärmbilden har sparats"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Det gick inte att spara skärmbilden"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Extern skärm"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parkoppla en ny enhet"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klicka för att parkoppla en ny enhet"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Det gick inte att uppdatera förinställningen"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vill du återaktivera enhetens kamera och mikrofon?"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index fab95f0..670a85c 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"imetuma picha"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Inahifadhi picha ya skrini..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Inahifadhi picha ya skrini kwenye wasifu wa kazini…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Inahifadhi picha ya skrini kwenye wasifu wa faragha"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Imehifadhi picha ya skrini"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Imeshindwa kuhifadhi picha ya skrini"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Skrini ya Nje"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Unganisha kifaa kipya"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Bofya ili uunganishe kifaa kipya"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Imeshindwa kusasisha mipangilio iliyowekwa mapema"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Ungependa kuwacha kuzuia kamera na maikrofoni ya kifaa?"</string>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index 2cfba01..29e0dbe 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -68,11 +68,6 @@
 
     <dimen name="qs_brightness_margin_bottom">16dp</dimen>
 
-    <!--  For large screens the security footer appears below the footer,
-    same as phones in portrait  -->
-    <dimen name="qs_security_footer_single_line_height">48dp</dimen>
-    <dimen name="qs_security_footer_background_inset">0dp</dimen>
-
     <dimen name="qs_panel_padding_top">8dp</dimen>
 
     <!-- The width of large/content heavy dialogs (e.g. Internet, Media output, etc) -->
@@ -102,6 +97,17 @@
     <dimen name="lockscreen_shade_status_bar_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen>
     <dimen name="lockscreen_shade_keyguard_transition_distance">@dimen/lockscreen_shade_media_transition_distance</dimen>
 
+    <!-- Dimensions for biometric prompt panel padding -->
+    <dimen name="biometric_prompt_one_pane_medium_top_guideline_padding">56dp</dimen>
+    <dimen name="biometric_prompt_one_pane_medium_horizontal_guideline_padding">@dimen/biometric_dialog_border_padding</dimen>
+
+    <!-- Dimensions for biometric prompt scroll view padding -->
+    <dimen name="biometric_prompt_top_scroll_view_bottom_padding">32dp</dimen>
+    <dimen name="biometric_prompt_top_scroll_view_horizontal_padding">32dp</dimen>
+
+    <!-- Dimensions for biometric prompt custom content view. -->
+    <dimen name="biometric_prompt_logo_description_top_padding">16dp</dimen>
+
     <!-- Biometric Auth pattern view size, better to align keyguard_security_width -->
     <dimen name="biometric_auth_pattern_view_size">348dp</dimen>
 
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index d8e6892..224ed398 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"படம் அனுப்பப்பட்டது"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"ஸ்க்ரீன் ஷாட்டைச் சேமிக்கிறது…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"பணிக் கணக்கில் ஸ்கிரீன்ஷாட் சேமிக்கப்படுகிறது…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"தனிப்பட்ட சுயவிவரத்தில் ஸ்கிரீன்ஷாட் சேமிக்கப்படுகிறது"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"ஸ்கிரீன்ஷாட் சேமிக்கப்பட்டது"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"ஸ்கிரீன் ஷாட்டைச் சேமிக்க முடியவில்லை"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"வெளித் திரை"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"சாதனத்தின் மைக்ரோஃபோனுக்கான தடுப்பை நீக்கவா?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"சாதனத்தின் கேமராவுக்கான தடுப்பை நீக்கவா?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"சாதனத்தின் கேமராவுக்கும் மைக்ரோஃபோனுக்குமான தடுப்பை நீக்கவா?"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 77c89c0..26bb871 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ఇమేజ్‌ను పంపారు"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"స్క్రీన్‌షాట్‌ను సేవ్ చేస్తోంది…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"స్క్రీన్‌షాట్‌ను వర్క్ ప్రొఫైల్‌కు సేవ్ చేస్తోంది…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"స్క్రీన్‌షాట్‌ను ప్రైవేట్ ప్రొఫైల్‌కు సేవ్ చేస్తోంది"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"స్క్రీన్‌షాట్ సేవ్ చేయబడింది"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"స్క్రీన్‌షాట్‌ని సేవ్ చేయడం సాధ్యం కాలేదు"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"వెలుపలి డిస్‌ప్లే"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"పరికరం మైక్రోఫోన్‌ను అన్‌బ్లాక్ చేయమంటారా?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"పరికరంలోని కెమెరాను అన్‌బ్లాక్ చేయమంటారా?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"పరికరంలోని కెమెరా, మైక్రోఫోన్‌లను అన్‌బ్లాక్ చేయమంటారా?"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 0800a26..02ee017 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ส่งรูปภาพ"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"กำลังบันทึกภาพหน้าจอ..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"กำลังบันทึกภาพหน้าจอไปยังโปรไฟล์งาน…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"กำลังบันทึกภาพหน้าจอลงในโปรไฟล์ส่วนตัว"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"บันทึกภาพหน้าจอแล้ว"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"บันทึกภาพหน้าจอไม่ได้"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"จอแสดงผลภายนอก"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"เลิกบล็อกไมโครโฟนของอุปกรณ์ใช่ไหม"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"เลิกบล็อกกล้องของอุปกรณ์ใช่ไหม"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"เลิกบล็อกกล้องและไมโครโฟนของอุปกรณ์ใช่ไหม"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index a8cba0b..aabba45 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"nagpadala ng larawan"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Sine-save ang screenshot…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Sine-save ang screenshot sa profile sa trabaho…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Sine-save ang screenshot sa pribado"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Na-save ang screenshot"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Hindi ma-save ang screenshot"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"External na Display"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Magpares ng bagong device"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"I-click para magpares ng bagong device"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Hindi ma-update ang preset"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"I-unblock ang camera at mikropono ng device?"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index d3fceeb..db84644 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"bir resim gönderildi"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Ekran görüntüsü kaydediliyor..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Ekran görüntüsü iş profiline kaydediliyor…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Ekran görüntüsü özel profile kaydediliyor"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Ekran görüntüsü kaydedildi"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Ekran görüntüsü kaydedilemedi"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Harici Ekran"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Yeni cihaz eşle"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yeni cihaz eşlemek için tıklayın"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Hazır ayar güncellenemedi"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Cihaz kamerası ile mikrofonunun engellemesi kaldırılsın mı?"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 662031c..1afcf2a 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"надіслане зображення"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Збереження знімка екрана..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Зберігання знімка екрана в робочому профілі…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Збереження знімка екрана в приватному профілі"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Знімок екрана збережено"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Не вдалося зберегти знімок екрана"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Зовнішній дисплей"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Надати доступ до мікрофона?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Надати доступ до камери пристрою?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Надати доступ до камери й мікрофона?"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 24a3eb0..d7c4c88 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"ایک تصویر بھیجی"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"اسکرین شاٹ محفوظ ہو رہا ہے…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"اسکرین شاٹ دفتری پروفائل میں محفوظ کیا جا رہا ہے…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"اسکرین شاٹ کو نجی پروفائل میں محفوظ کیا جا رہا ہے"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"اسکرین شاٹ محفوظ ہو گیا"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"اسکرین شاٹ کو محفوظ نہیں کیا جا سکا"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"خارجی ڈسپلے"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"آلے کا مائیکروفون غیر مسدود کریں؟"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"آلے کا کیمرا غیر مسدود کریں؟"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"آلے کا کیمرا اور مائیکروفون غیر مسدود کریں؟"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 5cdff8b..55bdd84 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"rasm yuborildi"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Skrinshot saqlanmoqda…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Skrinshot ish profiliga saqlanmoqda…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Skrinshot shaxsiy profilga saqlanmoqda"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Skrinshot saqlandi"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Skrinshot saqlanmadi"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Tashqi displey"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Yangi qurilmani ulash"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yangi qurilmani ulash uchun bosing"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Andoza yangilanmadi"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Qurilma kamerasi va mikrofoni blokdan chiqarilsinmi?"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 7c91916..fb1f28c 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"đã gửi hình ảnh"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Đang lưu ảnh chụp màn hình..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Đang lưu ảnh chụp màn hình vào hồ sơ công việc…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Đang lưu ảnh chụp màn hình vào hồ sơ riêng tư"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Đã lưu ảnh chụp màn hình"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Không thể lưu ảnh chụp màn hình"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Màn hình bên ngoài"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Ghép nối thiết bị mới"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Nhấp để ghép nối thiết bị mới"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Không cập nhật được giá trị đặt trước"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Bỏ chặn máy ảnh và micrô của thiết bị?"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index dda17d4..1a3055c 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"发送了一张图片"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"正在保存屏幕截图..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"正在将屏幕截图保存到工作资料…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"正在将屏幕截图保存到个人资料"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"已保存屏幕截图"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"无法保存屏幕截图"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"外部显示屏"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解锁设备麦克风吗?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"要解锁设备摄像头吗?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要解锁设备摄像头和麦克风吗?"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index f48d322..8523972 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"已傳送圖片"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"正在儲存螢幕擷取畫面..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"正在將螢幕截圖儲存至工作設定檔…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"正在將螢幕截圖儲存至個人設定檔"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"螢幕擷取畫面已儲存"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"無法儲存螢幕擷取畫面"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"外部顯示屏"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解除封鎖裝置麥克風嗎?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"要解除封鎖裝置相機嗎?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要解除封鎖裝置相機和麥克風嗎?"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index af1d915..8d5cad4 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"傳送了一張圖片"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"正在儲存螢幕截圖…"</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"正在將螢幕截圖儲存到工作資料夾…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"正在將螢幕截圖儲存到個人資料夾"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"螢幕截圖已儲存"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"無法儲存螢幕截圖"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"外接螢幕"</string>
@@ -278,7 +279,7 @@
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"取消連結"</string>
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"啟用"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自動重新開啟"</string>
-    <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"「快速分享」和「尋找我的裝置」等功能需要藍牙"</string>
+    <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"「快速分享」和「尋找我的裝置」等功能都需要使用藍牙技術"</string>
     <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"藍牙會在明天早上開啟"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"音訊分享"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"正在分享音訊"</string>
@@ -371,6 +372,8 @@
     <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>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解除封鎖裝置麥克風嗎?"</string>
     <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"要解除封鎖裝置相機嗎?"</string>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要將裝置的相機和麥克風解除封鎖嗎?"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index a4673db..6644b53 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -76,6 +76,7 @@
     <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"uthumele isithombe"</string>
     <string name="screenshot_saving_title" msgid="2298349784913287333">"Ilondoloz umfanekiso weskrini..."</string>
     <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"Ilondoloza isithombe-skrini kuphrofayela yomsebenzi…"</string>
+    <string name="screenshot_saving_private_profile" msgid="8934706048497093297">"Ilondoloza isithombe-skrini sibe esigodliwe"</string>
     <string name="screenshot_saved_title" msgid="8893267638659083153">"Isithombe-skrini silondoloziwe"</string>
     <string name="screenshot_failed_title" msgid="3259148215671936891">"Ayikwazanga ukulondoloza isithombe-skrini"</string>
     <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"Isiboniso Sangaphandle"</string>
@@ -271,7 +272,7 @@
     <string name="quick_settings_bluetooth_tile_subtitle" msgid="212752719010829550">"Thepha ukuze uxhumae noma ungaxhumi idivaysi"</string>
     <string name="pair_new_bluetooth_devices" msgid="4601767620843349645">"Bhangqa idivayisi entsha"</string>
     <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Buka konke"</string>
-    <string name="turn_on_bluetooth" msgid="5681370462180289071">"Sebenzisa i-Bluetooth"</string>
+    <string name="turn_on_bluetooth" msgid="5681370462180289071">"Sebenzisa iBluetooth"</string>
     <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ixhunyiwe"</string>
     <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Ukwabelana Ngokuqoshiwe"</string>
     <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Ilondoloziwe"</string>
@@ -279,7 +280,7 @@
     <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"yenza kusebenze"</string>
     <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Vula ngokuzenzekela futhi kusasa"</string>
     <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Izakhi ezifana nokuthi Ukwabelana Ngokushesha kanye nokuthi Thola Idivayisi Yami zisebenzisa i-Bluetooth"</string>
-    <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"I-Bluetooth izovuleka kusasa ekuseni"</string>
+    <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"IBluetooth izovuleka kusasa ekuseni"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Ukwabelana Ngokuqoshiwe"</string>
     <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Ukwabelana Ngomsindo"</string>
     <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ibhethri"</string>
@@ -371,6 +372,8 @@
     <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Bhangqa idivayisi entsha"</string>
     <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Chofoza ukuze ubhangqe idivayisi entsha"</string>
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ayikwazanga ukubuyekeza ukusetha ngaphambilini"</string>
+    <!-- no translation found for hearing_devices_preset_label (7878267405046232358) -->
+    <skip />
     <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>
     <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"Vulela ikhamera yedivayisi nemakrofoni?"</string>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 638785402..fb88364 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -125,6 +125,20 @@
     <!-- Use collapsed layout for media player in landscape QQS -->
     <bool name="config_quickSettingsMediaLandscapeCollapsed">true</bool>
 
+    <!-- For hearing devices related tool list. Need to be in ComponentName format (package/class).
+         Should be activity to be launched.
+         Already contains tool that holds intent: "com.android.settings.action.live_caption".
+         Maximum number is 3. -->
+    <string-array name="config_quickSettingsHearingDevicesRelatedToolName" translatable="false">
+    </string-array>
+
+    <!-- The drawable resource names. If provided, it will replace the corresponding icons in
+         config_quickSettingsHearingDevicesRelatedToolName. Can be empty to use original icons.
+         Already contains tool that holds intent: "com.android.settings.action.live_caption".
+         Maximum number is 3. -->
+    <string-array name="config_quickSettingsHearingDevicesRelatedToolIcon" translatable="false">
+    </string-array>
+
     <!-- Show indicator for Wifi on but not connected. -->
     <bool name="config_showWifiIndicatorWhenEnabled">false</bool>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 7d7a5d4..8ce2068 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -713,7 +713,6 @@
     <dimen name="qs_header_mobile_icon_size">@dimen/status_bar_icon_drawing_size</dimen>
     <dimen name="qs_header_carrier_separator_width">6dp</dimen>
     <dimen name="qs_carrier_margin_width">4dp</dimen>
-    <dimen name="qs_footer_icon_size">20dp</dimen>
     <dimen name="qs_header_height">120dp</dimen>
     <dimen name="qs_header_row_min_height">48dp</dimen>
 
@@ -721,11 +720,7 @@
     <dimen name="new_qs_header_non_clickable_element_height">24sp</dimen>
 
     <dimen name="qs_footer_padding">20dp</dimen>
-    <dimen name="qs_security_footer_height">88dp</dimen>
-    <dimen name="qs_security_footer_single_line_height">48dp</dimen>
     <dimen name="qs_footers_margin_bottom">8dp</dimen>
-    <dimen name="qs_security_footer_background_inset">0dp</dimen>
-    <dimen name="qs_security_footer_corner_radius">28dp</dimen>
 
     <dimen name="segmented_button_spacing">0dp</dimen>
     <dimen name="borderless_button_radius">2dp</dimen>
@@ -1119,11 +1114,18 @@
     <dimen name="biometric_dialog_height">240dp</dimen>
 
     <!-- Dimensions for biometric prompt panel padding -->
-    <dimen name="biometric_prompt_small_horizontal_guideline_padding">344dp</dimen>
-    <dimen name="biometric_prompt_udfps_horizontal_guideline_padding">114dp</dimen>
-    <dimen name="biometric_prompt_udfps_mid_guideline_padding">409dp</dimen>
-    <dimen name="biometric_prompt_medium_horizontal_guideline_padding">640dp</dimen>
-    <dimen name="biometric_prompt_medium_mid_guideline_padding">330dp</dimen>
+    <dimen name="biometric_prompt_panel_max_width">640dp</dimen>
+    <dimen name="biometric_prompt_land_small_horizontal_guideline_padding">344dp</dimen>
+    <dimen name="biometric_prompt_two_pane_udfps_horizontal_guideline_padding">114dp</dimen>
+    <dimen name="biometric_prompt_two_pane_udfps_mid_guideline_padding">409dp</dimen>
+    <dimen name="biometric_prompt_two_pane_medium_horizontal_guideline_padding">640dp</dimen>
+    <dimen name="biometric_prompt_two_pane_medium_mid_guideline_padding">330dp</dimen>
+    <dimen name="biometric_prompt_one_pane_medium_top_guideline_padding">119dp</dimen>
+    <dimen name="biometric_prompt_one_pane_medium_horizontal_guideline_padding">0dp</dimen>
+
+    <!-- Dimensions for biometric prompt scroll view padding -->
+    <dimen name="biometric_prompt_top_scroll_view_bottom_padding">24dp</dimen>
+    <dimen name="biometric_prompt_top_scroll_view_horizontal_padding">24dp</dimen>
 
     <!-- Dimensions for biometric prompt icon padding -->
     <dimen name="biometric_prompt_portrait_small_bottom_padding">60dp</dimen>
@@ -1136,6 +1138,7 @@
 
     <!-- Dimensions for biometric prompt custom content view. -->
     <dimen name="biometric_prompt_logo_size">32dp</dimen>
+    <dimen name="biometric_prompt_logo_description_top_padding">8dp</dimen>
     <dimen name="biometric_prompt_content_corner_radius">28dp</dimen>
     <dimen name="biometric_prompt_content_padding_horizontal">24dp</dimen>
     <dimen name="biometric_prompt_content_padding_vertical">16dp</dimen>
@@ -1778,6 +1781,9 @@
     <dimen name="hearing_devices_preset_spinner_text_padding_vertical">15dp</dimen>
     <dimen name="hearing_devices_preset_spinner_arrow_size">24dp</dimen>
     <dimen name="hearing_devices_preset_spinner_background_radius">28dp</dimen>
+    <dimen name="hearing_devices_tool_icon_frame_width">94dp</dimen>
+    <dimen name="hearing_devices_tool_icon_frame_height">64dp</dimen>
+    <dimen name="hearing_devices_tool_icon_size">28dp</dimen>
 
     <!-- Height percentage of the parent container occupied by the communal view -->
     <item name="communal_source_height_percentage" format="float" type="dimen">0.80</item>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index b993a5a..177ba598 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -259,9 +259,6 @@
     <!-- ID of the Scene Container root Composable view -->
     <item type='id' name="scene_container_root_composable" />
 
-    <!-- Tag set on the Compose implementation of the QS footer actions. -->
-    <item type="id" name="tag_compose_qs_footer_actions" />
-
     <!--
     Ids for the device entry icon.
         device_entry_icon_view: parent view of both device_entry_icon and device_entry_icon_bg
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 6f2806d..03c7d9b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -895,11 +895,9 @@
     <string name="qs_record_issue_dropdown_screenrecord">Screen record</string>
 
     <!-- QuickSettings: Issue Type Drop down choices list in Record Issue Start Dialog [CHAR LIMIT=30] -->
-    <string-array name="qs_record_issue_types">
-        <item>Performance</item>
-        <item>User Interface</item>
-        <item>Battery</item>
-    </string-array>
+    <string name="performance">Performance</string>
+    <string name="user_interface">User Interface</string>
+    <string name="thermal">Thermal</string>
 
     <!-- QuickSettings: Label for the toggle that controls whether One-handed mode is enabled. [CHAR LIMIT=NONE] -->
     <string name="quick_settings_onehanded_label">One-handed mode</string>
@@ -917,6 +915,8 @@
     <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: Tool name for hearing devices dialog related tools [CHAR LIMIT=40]-->
+    <string name="live_caption_title">Live Caption</string>
 
     <!--- Title of dialog triggered if the microphone is disabled but an app tried to access it. [CHAR LIMIT=150] -->
     <string name="sensor_privacy_start_use_mic_dialog_title">Unblock device microphone?</string>
@@ -3487,6 +3487,45 @@
     <!-- Label for recent app usage of a phone sensor with sub-attribution and proxy label in the privacy dialog [CHAR LIMIT=NONE] -->
     <string name="privacy_dialog_recent_app_usage_2">Recently used by <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> (<xliff:g id="attribution_label" example="For Wallet">%2$s</xliff:g> \u2022 <xliff:g id="proxy_label" example="Speech services">%3$s</xliff:g>)</string>
 
+    <!-- Title of the keyboard shortcut helper category "System". The helper is a component that
+         shows the user which keyboard shortcuts they can use. The "System" shortcuts are for
+         example "Take a screenshot" or "Go back". [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_category_system">System</string>
+    <!-- Title of the keyboard shortcut helper category "Multitasking". The helper is a component
+         that shows the user which keyboard shortcuts they can use. The "Multitasking" shortcuts are
+         for example "Enter split screen". [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_category_multitasking">Multitasking</string>
+    <!-- Title of the keyboard shortcut helper category "Input". The helper is a component
+         that shows the user which keyboard shortcuts they can use. The "Input" shortcuts are
+         the ones provided by the keyboard. Examples are "Access emoji" or "Switch to next language"
+         [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_category_input">Input</string>
+    <!-- Title of the keyboard shortcut helper category "App shortcuts". The helper is a component
+         that shows the user which keyboard shortcuts they can use. The "App shortcuts" are
+         for example "Open browser" or "Open calculator". [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_category_app_shortcuts">App shortcuts</string>
+    <!-- Title of the keyboard shortcut helper category "Accessibility". The helper is a component
+         that shows the user which keyboard shortcuts they can use. The "Accessibility" shortcuts
+         are for example "Turn on talkback". [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_category_a11y">Accessibility</string>
+    <!-- Title at the top of the keyboard shortcut helper UI. The helper is a component
+         that shows the user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_title">Keyboard shortcuts</string>
+    <!-- Placeholder text shown in the search box of the keyboard shortcut helper, when the user
+         hasn't typed in anything in the search box yet. The helper is a  component that shows the
+         user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_search_placeholder">Search shortcuts</string>
+    <!-- Content description of the icon that allows to collapse a keyboard shortcut helper category
+         panel. The helper is a  component that shows the  user which keyboard shortcuts they can
+         use. The helper shows shortcuts in categories, which can be collapsed or expanded.
+         [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_content_description_collapse_icon">Collapse icon</string>
+    <!-- Content description of the icon that allows to expand a keyboard shortcut helper category
+         panel. The helper is a  component that shows the  user which keyboard shortcuts they can
+         use. The helper shows shortcuts in categories, which can be collapsed or expanded.
+         [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_content_description_expand_icon">Expand icon</string>
+
     <!-- Content description for keyboard backlight brightness dialog [CHAR LIMIT=NONE] -->
     <string name="keyboard_backlight_dialog_title">Keyboard backlight</string>
     <!-- Content description for keyboard backlight brightness value [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index b8f71c1..1e0adec 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -149,11 +149,6 @@
         <item name="android:letterSpacing">0.01</item>
     </style>
 
-    <style name="TextAppearance.QS.SecurityFooter" parent="@style/TextAppearance.QS.Status">
-        <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
-        <item name="android:textColor">?attr/onSurface</item>
-    </style>
-
     <style name="TextAppearance.QS.Status.Carriers" />
 
     <style name="TextAppearance.QS.Status.Carriers.NoCarrierText">
@@ -1670,6 +1665,10 @@
         <item name="android:colorBackground">@color/transparent</item>
     </style>
 
+    <style name="ShortcutHelperBottomSheet" parent="@style/Widget.Material3.BottomSheet">
+        <item name="backgroundTint">?colorSurfaceContainer</item>
+    </style>
+
     <style name="ShortcutHelperAnimation" parent="@android:style/Animation.Activity">
         <item name="android:activityOpenEnterAnimation">@anim/shortcut_helper_launch_anim</item>
         <item name="android:taskOpenEnterAnimation">@anim/shortcut_helper_launch_anim</item>
diff --git a/packages/SystemUI/res/xml/fileprovider.xml b/packages/SystemUI/res/xml/fileprovider.xml
index 71cc05d..78b7e95 100644
--- a/packages/SystemUI/res/xml/fileprovider.xml
+++ b/packages/SystemUI/res/xml/fileprovider.xml
@@ -19,5 +19,4 @@
     <cache-path name="leak" path="leak/"/>
     <external-path name="screenrecord" path="."/>
     <cache-path name="multi_user" path="multi_user/" />
-    <root-path name="traces" path="/data/local/traces"/>
 </paths>
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/PromptKind.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/PromptKind.kt
index b99c514..44f2059 100644
--- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/PromptKind.kt
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/PromptKind.kt
@@ -22,15 +22,28 @@
     data class Biometric(
         /** The available modalities for the authentication on the prompt. */
         val activeModalities: BiometricModalities = BiometricModalities(),
-        // TODO(b/330908557): Use this value to decide whether to show two pane layout, instead of
-        // simply depending on rotations.
-        val showTwoPane: Boolean = false,
-    ) : PromptKind
+        val paneType: PaneType = PaneType.ONE_PANE_PORTRAIT,
+    ) : PromptKind {
+        enum class PaneType {
+            TWO_PANE_LANDSCAPE,
+            ONE_PANE_PORTRAIT,
+            ONE_PANE_NO_SENSOR_LANDSCAPE,
+            ONE_PANE_LARGE_SCREEN_LANDSCAPE
+        }
+    }
 
-    object Pin : PromptKind
-    object Pattern : PromptKind
-    object Password : PromptKind
+    data object Pin : PromptKind
+    data object Pattern : PromptKind
+    data object Password : PromptKind
 
     fun isBiometric() = this is Biometric
-    fun isCredential() = (this is Pin) or (this is Pattern) or (this is Password)
+    fun isTwoPaneLandscapeBiometric(): Boolean =
+        (this as? Biometric)?.paneType == Biometric.PaneType.TWO_PANE_LANDSCAPE
+    fun isOnePanePortraitBiometric() =
+        (this as? Biometric)?.paneType == Biometric.PaneType.ONE_PANE_PORTRAIT
+    fun isOnePaneNoSensorLandscapeBiometric() =
+        (this as? Biometric)?.paneType == Biometric.PaneType.ONE_PANE_NO_SENSOR_LANDSCAPE
+    fun isOnePaneLargeScreenLandscapeBiometric() =
+        (this as? Biometric)?.paneType == Biometric.PaneType.ONE_PANE_LARGE_SCREEN_LANDSCAPE
+    fun isCredential() = (this is Pin) || (this is Pattern) || (this is Password)
 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
index 1366226..e8eb53f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.flags
 
 import android.app.Activity
+import android.content.pm.PackageManager
 import android.content.BroadcastReceiver
 import android.content.Context
 import android.content.Intent
@@ -35,6 +36,7 @@
 ) : FlagListenable {
     companion object {
         const val RECEIVING_PACKAGE = "com.android.systemui"
+        const val RECEIVING_PACKAGE_WATCH = "com.google.android.apps.wearable.systemui"
         const val ACTION_SET_FLAG = "com.android.systemui.action.SET_FLAG"
         const val ACTION_GET_FLAGS = "com.android.systemui.action.GET_FLAGS"
         const val FLAGS_PERMISSION = "com.android.systemui.permission.FLAGS"
@@ -62,7 +64,7 @@
 
     fun getFlagsFuture(): ListenableFuture<Collection<Flag<*>>> {
         val intent = Intent(ACTION_GET_FLAGS)
-        intent.setPackage(RECEIVING_PACKAGE)
+        intent.setPackage(if (isWatch()) RECEIVING_PACKAGE_WATCH else RECEIVING_PACKAGE)
 
         return CallbackToFutureAdapter.getFuture {
                 completer: CallbackToFutureAdapter.Completer<Collection<Flag<*>>> ->
@@ -193,6 +195,10 @@
         restartAction?.accept(suppressRestart)
     }
 
+    private fun isWatch(): Boolean {
+        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)
+    }
+
     private data class PerFlagListener(val name: String, val listener: FlagListenable.Listener)
 }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
index 3bf3fb3..b116e29 100644
--- a/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/CarrierTextManager.java
@@ -308,7 +308,13 @@
             } else {
                 // Don't listen and clear out the text when the device isn't a phone.
                 mMainExecutor.execute(() -> callback.updateCarrierInfo(
-                        new CarrierTextCallbackInfo("", null, false, null)
+                        new CarrierTextCallbackInfo(
+                                /* carrierText= */ "",
+                                /* listOfCarriers= */ null,
+                                /* anySimReady= */ false,
+                                /* isInSatelliteMode= */ false,
+                                /* subscriptionIds= */ null,
+                                /* airplaneMode= */ false)
                 ));
             }
         } else {
@@ -448,10 +454,12 @@
             displayText = currentSatelliteText;
         }
 
+        boolean isInSatelliteMode = mSatelliteCarrierText != null;
         final CarrierTextCallbackInfo info = new CarrierTextCallbackInfo(
                 displayText,
                 carrierNames,
                 !allSimsMissing,
+                isInSatelliteMode,
                 subsIds,
                 airplaneMode);
         mLogger.logCallbackSentFromUpdate(info);
@@ -757,21 +765,35 @@
         public final CharSequence carrierText;
         public final CharSequence[] listOfCarriers;
         public final boolean anySimReady;
+        public final boolean isInSatelliteMode;
         public final int[] subscriptionIds;
         public boolean airplaneMode;
 
         @VisibleForTesting
-        public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers,
-                boolean anySimReady, int[] subscriptionIds) {
-            this(carrierText, listOfCarriers, anySimReady, subscriptionIds, false);
+        public CarrierTextCallbackInfo(
+                CharSequence carrierText,
+                CharSequence[] listOfCarriers,
+                boolean anySimReady,
+                int[] subscriptionIds) {
+            this(carrierText,
+                    listOfCarriers,
+                    anySimReady,
+                    /* isInSatelliteMode= */ false,
+                    subscriptionIds,
+                    /* airplaneMode= */ false);
         }
 
-        @VisibleForTesting
-        public CarrierTextCallbackInfo(CharSequence carrierText, CharSequence[] listOfCarriers,
-                boolean anySimReady, int[] subscriptionIds, boolean airplaneMode) {
+        public CarrierTextCallbackInfo(
+                CharSequence carrierText,
+                CharSequence[] listOfCarriers,
+                boolean anySimReady,
+                boolean isInSatelliteMode,
+                int[] subscriptionIds,
+                boolean airplaneMode) {
             this.carrierText = carrierText;
             this.listOfCarriers = listOfCarriers;
             this.anySimReady = anySimReady;
+            this.isInSatelliteMode = isInSatelliteMode;
             this.subscriptionIds = subscriptionIds;
             this.airplaneMode = airplaneMode;
         }
@@ -782,6 +804,7 @@
                     + "carrierText=" + carrierText
                     + ", listOfCarriers=" + Arrays.toString(listOfCarriers)
                     + ", anySimReady=" + anySimReady
+                    + ", isInSatelliteMode=" + isInSatelliteMode
                     + ", subscriptionIds=" + Arrays.toString(subscriptionIds)
                     + ", airplaneMode=" + airplaneMode
                     + '}';
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 3f3bb0b..86c807b 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -15,12 +15,12 @@
  */
 package com.android.keyguard
 
-import android.os.Trace
 import android.content.BroadcastReceiver
 import android.content.Context
 import android.content.Intent
 import android.content.IntentFilter
 import android.content.res.Resources
+import android.os.Trace
 import android.text.format.DateFormat
 import android.util.Log
 import android.util.TypedValue
@@ -466,15 +466,11 @@
         largeRegionSampler?.stopRegionSampler()
         smallTimeListener?.stop()
         largeTimeListener?.stop()
-        clock
-            ?.smallClock
-            ?.view
-            ?.removeOnAttachStateChangeListener(smallClockOnAttachStateChangeListener)
+        clock?.apply {
+            smallClock.view.removeOnAttachStateChangeListener(smallClockOnAttachStateChangeListener)
+            largeClock.view.removeOnAttachStateChangeListener(largeClockOnAttachStateChangeListener)
+        }
         smallClockFrame?.viewTreeObserver?.removeOnGlobalLayoutListener(onGlobalLayoutListener)
-        clock
-            ?.largeClock
-            ?.view
-            ?.removeOnAttachStateChangeListener(largeClockOnAttachStateChangeListener)
     }
 
     /**
@@ -505,15 +501,17 @@
         }
     }
 
-    private fun updateFontSizes() {
+    fun updateFontSizes() {
         clock?.run {
-            smallClock.events.onFontSettingChanged(
-                resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
-            )
+            smallClock.events.onFontSettingChanged(getSmallClockSizePx())
             largeClock.events.onFontSettingChanged(getLargeClockSizePx())
         }
     }
 
+    private fun getSmallClockSizePx(): Float {
+        return resources.getDimensionPixelSize(R.dimen.small_clock_text_size).toFloat()
+    }
+
     private fun getLargeClockSizePx(): Float {
         return if (largeClockOnSecondaryDisplay) {
             resources.getDimensionPixelSize(R.dimen.presentation_clock_text_size).toFloat()
@@ -549,9 +547,8 @@
                         it.copy(value = 1f - it.value)
                     },
                     keyguardTransitionInteractor.transition(Edge.create(LOCKSCREEN, AOD)),
-                ).filter {
-                    it.transitionState != TransitionState.FINISHED
-                }
+                )
+                .filter { it.transitionState != TransitionState.FINISHED }
                 .collect { handleDoze(it.value) }
         }
     }
@@ -574,28 +571,27 @@
     internal fun listenForAnyStateToLockscreenTransition(scope: CoroutineScope): Job {
         return scope.launch {
             keyguardTransitionInteractor
-                    .transitionStepsToState(LOCKSCREEN)
-                    .filter { it.transitionState == TransitionState.STARTED }
-                    .filter { it.from != AOD }
-                    .collect { handleDoze(0f) }
+                .transitionStepsToState(LOCKSCREEN)
+                .filter { it.transitionState == TransitionState.STARTED }
+                .filter { it.from != AOD }
+                .collect { handleDoze(0f) }
         }
     }
 
     /**
-     * When keyguard is displayed due to pulsing notifications when AOD is off,
-     * we should make sure clock is in dozing state instead of LS state
+     * When keyguard is displayed due to pulsing notifications when AOD is off, we should make sure
+     * clock is in dozing state instead of LS state
      */
     @VisibleForTesting
     internal fun listenForAnyStateToDozingTransition(scope: CoroutineScope): Job {
         return scope.launch {
             keyguardTransitionInteractor
-                    .transitionStepsToState(DOZING)
-                    .filter { it.transitionState == TransitionState.FINISHED }
-                    .collect { handleDoze(1f) }
+                .transitionStepsToState(DOZING)
+                .filter { it.transitionState == TransitionState.FINISHED }
+                .collect { handleDoze(1f) }
         }
     }
 
-
     @VisibleForTesting
     internal fun listenForDozing(scope: CoroutineScope): Job {
         return scope.launch {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 47e4b49..f688d4f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -308,14 +308,14 @@
         mStatusArea = mView.findViewById(R.id.keyguard_status_area);
 
         mBgExecutor.execute(() -> {
-            mSecureSettings.registerContentObserverForUser(
+            mSecureSettings.registerContentObserverForUserSync(
                     Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK,
                     false, /* notifyForDescendants */
                     mDoubleLineClockObserver,
                     UserHandle.USER_ALL
             );
 
-            mSecureSettings.registerContentObserverForUser(
+            mSecureSettings.registerContentObserverForUserSync(
                     Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED,
                     false, /* notifyForDescendants */
                     mShowWeatherObserver,
@@ -372,8 +372,8 @@
         setClock(null);
 
         mBgExecutor.execute(() -> {
-            mSecureSettings.unregisterContentObserver(mDoubleLineClockObserver);
-            mSecureSettings.unregisterContentObserver(mShowWeatherObserver);
+            mSecureSettings.unregisterContentObserverSync(mDoubleLineClockObserver);
+            mSecureSettings.unregisterContentObserverSync(mShowWeatherObserver);
         });
 
         mKeyguardUnlockAnimationController.removeKeyguardUnlockAnimationListener(
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 27b2b92..63ad41a 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -20,7 +20,6 @@
 import static androidx.dynamicanimation.animation.FloatPropertyCompat.createFloatPropertyCompat;
 
 import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
-import static com.android.systemui.flags.Flags.SWIPE_UNCLEARED_TRANSIENT_VIEW_FIX;
 import static com.android.systemui.statusbar.notification.NotificationUtils.logKey;
 
 import android.animation.Animator;
@@ -481,16 +480,11 @@
                 updateSwipeProgressFromOffset(animView, canBeDismissed);
                 mDismissPendingMap.remove(animView);
                 boolean wasRemoved = false;
-                if (animView instanceof ExpandableNotificationRow) {
-                    ExpandableNotificationRow row = (ExpandableNotificationRow) animView;
-                    if (mFeatureFlags.isEnabled(SWIPE_UNCLEARED_TRANSIENT_VIEW_FIX)) {
-                        // If the view is already removed from its parent and added as Transient,
-                        // we need to clean the transient view upon animation end
-                        wasRemoved = row.getTransientContainer() != null
-                            || row.getParent() == null || row.isRemoved();
-                    } else {
-                        wasRemoved = row.isRemoved();
-                    }
+                if (animView instanceof ExpandableNotificationRow row) {
+                    // If the view is already removed from its parent and added as Transient,
+                    // we need to clean the transient view upon animation end
+                    wasRemoved = row.getTransientContainer() != null
+                        || row.getParent() == null || row.isRemoved();
                 }
                 if (!mCancelled || wasRemoved) {
                     mCallback.onChildDismissed(animView);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
index ca24ccb..f2a68a8 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
@@ -319,7 +319,7 @@
         }
 
         // Unregister observer before removing view
-        mSecureSettings.unregisterContentObserver(mMagnificationCapabilityObserver);
+        mSecureSettings.unregisterContentObserverSync(mMagnificationCapabilityObserver);
         mWindowManager.removeView(mSettingView);
         mIsVisible = false;
         if (resetPosition) {
@@ -380,7 +380,7 @@
 
             mWindowManager.addView(mSettingView, mParams);
 
-            mSecureSettings.registerContentObserverForUser(
+            mSecureSettings.registerContentObserverForUserSync(
                     Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY,
                     mMagnificationCapabilityObserver,
                     UserHandle.USER_CURRENT);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepository.kt
index 4069cec..63791f9 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepository.kt
@@ -30,6 +30,7 @@
 import com.android.systemui.qs.tiles.ColorCorrectionTile
 import com.android.systemui.qs.tiles.ColorInversionTile
 import com.android.systemui.qs.tiles.FontScalingTile
+import com.android.systemui.qs.tiles.HearingDevicesTile
 import com.android.systemui.qs.tiles.OneHandedModeTile
 import com.android.systemui.qs.tiles.ReduceBrightColorsTile
 import javax.inject.Inject
@@ -74,6 +75,8 @@
                         .REDUCE_BRIGHT_COLORS_TILE_SERVICE_COMPONENT_NAME,
                 FontScalingTile.TILE_SPEC to
                     AccessibilityShortcutController.FONT_SIZE_TILE_COMPONENT_NAME,
+                HearingDevicesTile.TILE_SPEC to
+                    AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_TILE_COMPONENT_NAME
             )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
index eb840f1..ffb5f3d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
@@ -240,26 +240,26 @@
     }
 
     void registerObserversAndCallbacks() {
-        mSecureSettings.registerContentObserverForUser(
+        mSecureSettings.registerContentObserverForUserSync(
                 mSecureSettings.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS),
                 /* notifyForDescendants */ false, mMenuTargetFeaturesContentObserver,
                 UserHandle.USER_CURRENT);
         if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
-            mSecureSettings.registerContentObserverForUser(
+            mSecureSettings.registerContentObserverForUserSync(
                     mSecureSettings.getUriFor(ENABLED_ACCESSIBILITY_SERVICES),
                     /* notifyForDescendants */ false,
                     mMenuTargetFeaturesContentObserver,
                     UserHandle.USER_CURRENT);
         }
-        mSecureSettings.registerContentObserverForUser(
+        mSecureSettings.registerContentObserverForUserSync(
                 mSecureSettings.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE),
                 /* notifyForDescendants */ false, mMenuSizeContentObserver,
                 UserHandle.USER_CURRENT);
-        mSecureSettings.registerContentObserverForUser(
+        mSecureSettings.registerContentObserverForUserSync(
                 mSecureSettings.getUriFor(ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED),
                 /* notifyForDescendants */ false, mMenuFadeOutContentObserver,
                 UserHandle.USER_CURRENT);
-        mSecureSettings.registerContentObserverForUser(
+        mSecureSettings.registerContentObserverForUserSync(
                 mSecureSettings.getUriFor(ACCESSIBILITY_FLOATING_MENU_OPACITY),
                 /* notifyForDescendants */ false, mMenuFadeOutContentObserver,
                 UserHandle.USER_CURRENT);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegate.kt
index 91bc0c1..eaf541d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegate.kt
@@ -29,12 +29,12 @@
 import android.widget.TextView
 import androidx.annotation.MainThread
 import androidx.annotation.WorkerThread
-import com.android.systemui.res.R
 import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView
 import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView.OnSeekBarWithIconButtonsChangeListener
 import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView.OnSeekBarWithIconButtonsChangeListener.ControlUnitType
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.res.R
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.util.concurrency.DelayableExecutor
@@ -46,7 +46,9 @@
 import kotlin.math.roundToInt
 
 /** The Dialog that contains a seekbar for changing the font size. */
-class FontScalingDialogDelegate @Inject constructor(
+class FontScalingDialogDelegate
+@Inject
+constructor(
     private val context: Context,
     private val systemUIDialogFactory: SystemUIDialog.Factory,
     private val layoutInflater: LayoutInflater,
@@ -84,9 +86,9 @@
         dialog.setTitle(R.string.font_scaling_dialog_title)
         dialog.setView(layoutInflater.inflate(R.layout.font_scaling_dialog, null))
         dialog.setPositiveButton(
-                R.string.quick_settings_done,
-                /* onClick = */ null,
-                /* dismissOnClick = */ true
+            R.string.quick_settings_done,
+            /* onClick = */ null,
+            /* dismissOnClick = */ true
         )
     }
 
@@ -142,7 +144,7 @@
             }
         )
         doneButton.setOnClickListener { dialog.dismiss() }
-        systemSettings.registerContentObserver(Settings.System.FONT_SCALE, fontSizeObserver)
+        systemSettings.registerContentObserverSync(Settings.System.FONT_SCALE, fontSizeObserver)
     }
 
     /**
@@ -165,7 +167,7 @@
     override fun onStop(dialog: SystemUIDialog) {
         cancelUpdateFontScaleRunnable?.run()
         cancelUpdateFontScaleRunnable = null
-        systemSettings.unregisterContentObserver(fontSizeObserver)
+        systemSettings.unregisterContentObserverSync(fontSizeObserver)
     }
 
     @MainThread
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 28dd233..961d6aa 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -25,10 +25,14 @@
 import android.bluetooth.BluetoothHapPresetInfo;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
 import android.media.AudioManager;
 import android.os.Bundle;
 import android.os.Handler;
 import android.provider.Settings;
+import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.View.Visibility;
@@ -36,7 +40,10 @@
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
 import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.Spinner;
+import android.widget.TextView;
 import android.widget.Toast;
 
 import androidx.annotation.NonNull;
@@ -69,6 +76,7 @@
 import dagger.assisted.AssistedFactory;
 import dagger.assisted.AssistedInject;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 import java.util.stream.Collectors;
@@ -78,12 +86,15 @@
  */
 public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate,
         HearingDeviceItemCallback, BluetoothCallback {
-
+    private static final String TAG = "HearingDevicesDialogDelegate";
     @VisibleForTesting
     static final String ACTION_BLUETOOTH_DEVICE_DETAILS =
             "com.android.settings.BLUETOOTH_DEVICE_DETAIL_SETTINGS";
     private static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
     private static final String KEY_BLUETOOTH_ADDRESS = "device_address";
+    @VisibleForTesting
+    static final Intent LIVE_CAPTION_INTENT = new Intent(
+            "com.android.settings.action.live_caption");
     private final SystemUIDialog.Factory mSystemUIDialogFactory;
     private final DialogTransitionAnimator mDialogTransitionAnimator;
     private final ActivityStarter mActivityStarter;
@@ -102,6 +113,7 @@
     private Spinner mPresetSpinner;
     private ArrayAdapter<String> mPresetInfoAdapter;
     private Button mPairButton;
+    private LinearLayout mRelatedToolsContainer;
     private final HearingDevicesPresetsController.PresetCallback mPresetCallback =
             new HearingDevicesPresetsController.PresetCallback() {
                 @Override
@@ -253,10 +265,14 @@
         mPairButton = dialog.requireViewById(R.id.pair_new_device_button);
         mDeviceList = dialog.requireViewById(R.id.device_list);
         mPresetSpinner = dialog.requireViewById(R.id.preset_spinner);
+        mRelatedToolsContainer = dialog.requireViewById(R.id.related_tools_container);
 
         setupDeviceListView(dialog);
         setupPresetSpinner(dialog);
         setupPairNewDeviceButton(dialog, mShowPairNewDevice ? VISIBLE : GONE);
+        if (com.android.systemui.Flags.hearingDevicesDialogRelatedTools()) {
+            setupRelatedToolsView(dialog);
+        }
     }
 
     @Override
@@ -351,6 +367,34 @@
         }
     }
 
+    private void setupRelatedToolsView(SystemUIDialog dialog) {
+        final Context context = dialog.getContext();
+        final List<ToolItem> toolItemList = new ArrayList<>();
+        final String[] toolNameArray;
+        final String[] toolIconArray;
+
+        ToolItem preInstalledItem = getLiveCaption(context);
+        if (preInstalledItem != null) {
+            toolItemList.add(preInstalledItem);
+        }
+        try {
+            toolNameArray = context.getResources().getStringArray(
+                    R.array.config_quickSettingsHearingDevicesRelatedToolName);
+            toolIconArray = context.getResources().getStringArray(
+                    R.array.config_quickSettingsHearingDevicesRelatedToolIcon);
+            toolItemList.addAll(
+                    HearingDevicesToolItemParser.parseStringArray(context, toolNameArray,
+                    toolIconArray));
+        } catch (Resources.NotFoundException e) {
+            Log.i(TAG, "No hearing devices related tool config resource");
+        }
+        final int listSize = toolItemList.size();
+        for (int i = 0; i < listSize; i++) {
+            View view = createHearingToolView(context, toolItemList.get(i));
+            mRelatedToolsContainer.addView(view);
+        }
+    }
+
     private void refreshPresetInfoAdapter(List<BluetoothHapPresetInfo> presetInfos,
             int activePresetIndex) {
         mPresetInfoAdapter.clear();
@@ -400,6 +444,40 @@
         return null;
     }
 
+    @NonNull
+    private View createHearingToolView(Context context, ToolItem item) {
+        View view = LayoutInflater.from(context).inflate(R.layout.hearing_tool_item,
+                mRelatedToolsContainer, false);
+        ImageView icon = view.requireViewById(R.id.tool_icon);
+        TextView text = view.requireViewById(R.id.tool_name);
+        view.setContentDescription(item.getToolName());
+        icon.setImageDrawable(item.getToolIcon());
+        text.setText(item.getToolName());
+        Intent intent = item.getToolIntent();
+        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+        view.setOnClickListener(
+                v -> {
+                    dismissDialogIfExists();
+                    mActivityStarter.postStartActivityDismissingKeyguard(intent, /* delay= */ 0,
+                            mDialogTransitionAnimator.createActivityTransitionController(view));
+                });
+        return view;
+    }
+
+    private ToolItem getLiveCaption(Context context) {
+        final PackageManager packageManager = context.getPackageManager();
+        LIVE_CAPTION_INTENT.setPackage(packageManager.getSystemCaptionsServicePackageName());
+        final List<ResolveInfo> resolved = packageManager.queryIntentActivities(LIVE_CAPTION_INTENT,
+                /* flags= */ 0);
+        if (!resolved.isEmpty()) {
+            return new ToolItem(context.getString(R.string.live_caption_title),
+                    context.getDrawable(R.drawable.ic_volume_odi_captions),
+                    LIVE_CAPTION_INTENT);
+        }
+
+        return null;
+    }
+
     private void dismissDialogIfExists() {
         if (mDialog != null) {
             mDialog.dismiss();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java
new file mode 100644
index 0000000..2006726
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParser.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Utility class for managing and parsing tool items related to hearing devices.
+ */
+public class HearingDevicesToolItemParser {
+    private static final String TAG = "HearingDevicesToolItemParser";
+    private static final String SPLIT_DELIMITER = "/";
+    private static final String RES_TYPE = "drawable";
+    @VisibleForTesting
+    static final int MAX_NUM = 3;
+
+    /**
+     * Parses the string arrays to create a list of {@link ToolItem}.
+     *
+     * This method validates the structure of {@code toolNameArray} and {@code toolIconArray}.
+     * If {@code toolIconArray} is empty or mismatched in length with {@code toolNameArray}, the
+     * icon from {@link ActivityInfo#loadIcon(PackageManager)} will be used instead.
+     *
+     * @param context A valid context.
+     * @param toolNameArray An array of tool names in the format of {@link ComponentName}.
+     * @param toolIconArray An optional array of resource names for tool icons (can be empty).
+     * @return A list of {@link ToolItem} or an empty list if there are errors during parsing.
+     */
+    public static ImmutableList<ToolItem> parseStringArray(Context context, String[] toolNameArray,
+            String[] toolIconArray) {
+        if (toolNameArray.length == 0) {
+            Log.i(TAG, "Empty hearing device related tool name in array.");
+            return ImmutableList.of();
+        }
+        // For the performance concern, especially `getIdentifier` in `parseValidIcon`, we will
+        // limit the maximum number.
+        String[] nameArrayCpy = Arrays.copyOfRange(toolNameArray, 0,
+                Math.min(toolNameArray.length, MAX_NUM));
+        String[] iconArrayCpy = Arrays.copyOfRange(toolIconArray, 0,
+                Math.min(toolIconArray.length, MAX_NUM));
+
+        final PackageManager packageManager = context.getPackageManager();
+        final ImmutableList.Builder<ToolItem> toolItemList = ImmutableList.builder();
+        final List<ActivityInfo> activityInfoList = parseValidActivityInfo(context, nameArrayCpy);
+        final List<Drawable> iconList = parseValidIcon(context, iconArrayCpy);
+        final int size = activityInfoList.size();
+        // Only use custom icon if provided icon's list size is equal to provided name's list size.
+        final boolean useCustomIcons = (size == iconList.size());
+
+        for (int i = 0; i < size; i++) {
+            toolItemList.add(new ToolItem(
+                    activityInfoList.get(i).loadLabel(packageManager).toString(),
+                    useCustomIcons ? iconList.get(i)
+                            : activityInfoList.get(i).loadIcon(packageManager),
+                    new Intent(Intent.ACTION_MAIN).setComponent(
+                            activityInfoList.get(i).getComponentName())
+            ));
+        }
+
+        return toolItemList.build();
+    }
+
+    private static List<ActivityInfo> parseValidActivityInfo(Context context,
+            String[] toolNameArray) {
+        final PackageManager packageManager = context.getPackageManager();
+        final List<ActivityInfo> activityInfoList = new ArrayList<>();
+        for (String toolName : toolNameArray) {
+            String[] nameParts = toolName.split(SPLIT_DELIMITER);
+            if (nameParts.length == 2) {
+                ComponentName componentName = ComponentName.unflattenFromString(toolName);
+                try {
+                    ActivityInfo activityInfo = packageManager.getActivityInfo(
+                            componentName, /* flags= */ 0);
+                    activityInfoList.add(activityInfo);
+                } catch (PackageManager.NameNotFoundException e) {
+                    Log.e(TAG, "Unable to find hearing device related tool: "
+                            + componentName.flattenToString());
+                }
+            } else {
+                Log.e(TAG, "Malformed hearing device related tool name item in array: "
+                        + toolName);
+            }
+        }
+        return activityInfoList;
+    }
+
+    private static List<Drawable> parseValidIcon(Context context, String[] toolIconArray) {
+        final List<Drawable> drawableList = new ArrayList<>();
+        for (String icon : toolIconArray) {
+            int resId = context.getResources().getIdentifier(icon, RES_TYPE,
+                    context.getPackageName());
+            try {
+                drawableList.add(context.getDrawable(resId));
+            } catch (Resources.NotFoundException e) {
+                Log.e(TAG, "Resource does not exist: " + icon);
+            }
+        }
+        return drawableList;
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/ToolItem.kt
similarity index 71%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/ToolItem.kt
index 277dbb7..66bb2b5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/ToolItem.kt
@@ -14,8 +14,13 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.accessibility.hearingaid
 
-import com.android.systemui.kosmos.Kosmos
+import android.content.Intent
+import android.graphics.drawable.Drawable
 
-val Kosmos.iconLabelVisibilityRepository by Kosmos.Fixture { IconLabelVisibilityRepository() }
+data class ToolItem(
+    var toolName: String = "",
+    var toolIcon: Drawable,
+    var toolIntent: Intent,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.java
index 019f498..f905241 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/BouncerSwipeTouchHandler.java
@@ -269,6 +269,7 @@
             }
             mScrimManager.removeCallback(mScrimManagerCallback);
             mCapture = null;
+            mTouchSession = null;
 
             if (!Flags.communalBouncerDoNotModifyPluginOpen()) {
                 mNotificationShadeWindowController.setForcePluginOpen(false, this);
diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
index 227e4db..61b4401 100644
--- a/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/TouchMonitor.java
@@ -49,11 +49,14 @@
 
 import com.google.common.util.concurrent.ListenableFuture;
 
+import kotlinx.coroutines.Job;
+
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Set;
+import java.util.concurrent.CancellationException;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
@@ -78,15 +81,7 @@
     private final Lifecycle mLifecycle;
     private Rect mExclusionRect = null;
 
-    private ISystemGestureExclusionListener mGestureExclusionListener =
-            new ISystemGestureExclusionListener.Stub() {
-                @Override
-                public void onSystemGestureExclusionChanged(int displayId,
-                        Region systemGestureExclusion,
-                        Region systemGestureExclusionUnrestricted) {
-                    mExclusionRect = systemGestureExclusion.getBounds();
-                }
-            };
+    private ISystemGestureExclusionListener mGestureExclusionListener;
 
     private Consumer<Rect> mMaxBoundsConsumer = rect -> mMaxBounds = rect;
 
@@ -274,6 +269,14 @@
         if (bouncerAreaExclusion()) {
             mBackgroundExecutor.execute(() -> {
                 try {
+                    mGestureExclusionListener = new ISystemGestureExclusionListener.Stub() {
+                        @Override
+                        public void onSystemGestureExclusionChanged(int displayId,
+                                Region systemGestureExclusion,
+                                Region systemGestureExclusionUnrestricted) {
+                            mExclusionRect = systemGestureExclusion.getBounds();
+                        }
+                    };
                     mWindowManagerService.registerSystemGestureExclusionListener(
                             mGestureExclusionListener, mDisplayId);
                 } catch (RemoteException e) {
@@ -298,8 +301,11 @@
         if (bouncerAreaExclusion()) {
             mBackgroundExecutor.execute(() -> {
                 try {
-                    mWindowManagerService.unregisterSystemGestureExclusionListener(
-                            mGestureExclusionListener, mDisplayId);
+                    if (mGestureExclusionListener != null) {
+                        mWindowManagerService.unregisterSystemGestureExclusionListener(
+                                mGestureExclusionListener, mDisplayId);
+                        mGestureExclusionListener = null;
+                    }
                 } catch (RemoteException e) {
                     // Handle the exception
                     Log.e(TAG, "unregisterSystemGestureExclusionListener: failed", e);
@@ -494,6 +500,10 @@
 
     private Rect mMaxBounds;
 
+    private Job mBoundsFlow;
+
+    private boolean mInitialized;
+
 
     /**
      * Designated constructor for {@link TouchMonitor}
@@ -535,10 +545,35 @@
      * Initializes the monitor. should only be called once after creation.
      */
     public void init() {
+        if (mInitialized) {
+            throw new IllegalStateException("TouchMonitor already initialized");
+        }
+
         mLifecycle.addObserver(mLifecycleObserver);
         if (Flags.ambientTouchMonitorListenToDisplayChanges()) {
-            collectFlow(mLifecycle, mConfigurationInteractor.getMaxBounds(), mMaxBoundsConsumer);
+            mBoundsFlow = collectFlow(mLifecycle, mConfigurationInteractor.getMaxBounds(),
+                    mMaxBoundsConsumer);
         }
+
+        mInitialized = true;
+    }
+
+    /**
+     * Called when the TouchMonitor should be discarded and will not be used anymore.
+     */
+    public void destroy() {
+        if (!mInitialized) {
+            throw new IllegalStateException("TouchMonitor not initialized");
+        }
+
+        stopMonitoring(true);
+
+        mLifecycle.removeObserver(mLifecycleObserver);
+        if (Flags.ambientTouchMonitorListenToDisplayChanges()) {
+            mBoundsFlow.cancel(new CancellationException());
+        }
+
+        mInitialized = false;
     }
 
     private void isolate(Set<TouchSessionImpl> sessions) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index b75b292..1ee4908 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -29,6 +29,7 @@
 import android.annotation.Nullable;
 import android.app.AlertDialog;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.graphics.PixelFormat;
@@ -360,15 +361,23 @@
                 Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds),
                 Utils.findFirstSensorProperties(faceProps, mConfig.mSensorIds));
 
+        final boolean isLandscape = mContext.getResources().getConfiguration().orientation
+                == Configuration.ORIENTATION_LANDSCAPE;
         mPromptSelectorInteractorProvider = promptSelectorInteractorProvider;
         mPromptSelectorInteractorProvider.get().setPrompt(mConfig.mPromptInfo, mEffectiveUserId,
                 getRequestId(), biometricModalities, mConfig.mOperationId, mConfig.mOpPackageName,
-                false /*onSwitchToCredential*/);
+                false /*onSwitchToCredential*/, isLandscape);
 
         final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
-        if (constraintBp() && mPromptViewModel.getPromptKind().getValue().isBiometric()) {
-            mLayout = (ConstraintLayout) layoutInflater.inflate(
-                    R.layout.biometric_prompt_constraint_layout, this, false /* attachToRoot */);
+        final PromptKind kind = mPromptViewModel.getPromptKind().getValue();
+        if (constraintBp() && kind.isBiometric()) {
+            if (kind.isTwoPaneLandscapeBiometric()) {
+                mLayout = (ConstraintLayout) layoutInflater.inflate(
+                        R.layout.biometric_prompt_two_pane_layout, this, false /* attachToRoot */);
+            } else {
+                mLayout = (ConstraintLayout) layoutInflater.inflate(
+                        R.layout.biometric_prompt_one_pane_layout, this, false /* attachToRoot */);
+            }
         } else {
             mLayout = (FrameLayout) layoutInflater.inflate(
                     R.layout.auth_container_view, this, false /* attachToRoot */);
@@ -631,7 +640,7 @@
         if (fpProp != null && fpProp.isAnyUdfpsType()) {
             maybeUpdatePositionForUdfps(forceInvalidate /* invalidate */);
         }
-        if (faceProp != null && mBiometricView.isFaceOnly()) {
+        if (faceProp != null && mBiometricView != null && mBiometricView.isFaceOnly()) {
             alwaysUpdatePositionAtScreenBottom(forceInvalidate /* invalidate */);
         }
         if (fpProp != null && fpProp.sensorType == TYPE_POWER_BUTTON) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
index 8e5a97b..9b14d6f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/DisplayStateRepository.kt
@@ -29,11 +29,10 @@
 import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState.REAR_DISPLAY
 import com.android.systemui.display.data.repository.DisplayRepository
 import javax.inject.Inject
+import kotlin.math.min
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
@@ -58,7 +57,7 @@
     val currentDisplaySize: StateFlow<Size>
 
     /** Provides whether the current display is large screen */
-    val isLargeScreen: Flow<Boolean>
+    val isLargeScreen: StateFlow<Boolean>
 }
 
 @SysUISingleton
@@ -127,16 +126,29 @@
                     ),
             )
 
-    override val isLargeScreen: Flow<Boolean> =
+    override val isLargeScreen: StateFlow<Boolean> =
         currentDisplayInfo
             .map {
-                // TODO: This works, but investigate better way to handle this
-                it.logicalWidth * 160 / it.logicalDensityDpi > DisplayMetrics.DENSITY_XXXHIGH &&
-                    it.logicalHeight * 160 / it.logicalDensityDpi > DisplayMetrics.DENSITY_XXHIGH
+                // copied from systemui/shared/...Utilities.java
+                val smallestWidth =
+                    dpiFromPx(
+                        min(it.logicalWidth, it.logicalHeight).toFloat(),
+                        context.resources.configuration.densityDpi
+                    )
+                smallestWidth >= LARGE_SCREEN_MIN_DPS
             }
-            .distinctUntilChanged()
+            .stateIn(
+                backgroundScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = false,
+            )
+    private fun dpiFromPx(size: Float, densityDpi: Int): Float {
+        val densityRatio = densityDpi.toFloat() / DisplayMetrics.DENSITY_DEFAULT
+        return size / densityRatio
+    }
 
     companion object {
         const val TAG = "DisplayStateRepositoryImpl"
+        const val LARGE_SCREEN_MIN_DPS = 600f
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FaceUserSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FaceUserSettingsRepository.kt
index 68c4a10..2970890 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FaceUserSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FaceUserSettingsRepository.kt
@@ -75,7 +75,7 @@
 ) {
     fun fetch(): Boolean = getIntForUser(key, if (defaultValue) 1 else 0, userId) > 0
 
-    registerContentObserverForUser(
+    registerContentObserverForUserSync(
         key,
         false /* notifyForDescendants */,
         object : ContentObserver(handler) {
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 591da40..40313e3 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
@@ -65,7 +65,8 @@
     /** Called on configuration changes, used to keep the display state in sync */
     fun onConfigurationChanged(newConfig: Configuration)
 
-    val isLargeScreen: Flow<Boolean>
+    /** Provides whether the current display is large screen */
+    val isLargeScreen: StateFlow<Boolean>
 }
 
 /** Encapsulates logic for interacting with the display state. */
@@ -127,7 +128,7 @@
 
     override val isDefaultDisplayOff = displayRepository.defaultDisplayOff
 
-    override val isLargeScreen: Flow<Boolean> = displayStateRepository.isLargeScreen
+    override val isLargeScreen: StateFlow<Boolean> = displayStateRepository.isLargeScreen
 
     companion object {
         private const val TAG = "DisplayStateInteractor"
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt
index a74b0b0..b8ff3bb 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/FingerprintPropertyInteractor.kt
@@ -98,11 +98,11 @@
         ) { unscaledSensorLocation, scale ->
             val sensorLocation =
                 SensorLocation(
-                    unscaledSensorLocation.sensorLocationX,
-                    unscaledSensorLocation.sensorLocationY,
-                    unscaledSensorLocation.sensorRadius,
+                    naturalCenterX = unscaledSensorLocation.sensorLocationX,
+                    naturalCenterY = unscaledSensorLocation.sensorLocationY,
+                    naturalRadius = unscaledSensorLocation.sensorRadius,
+                    scale = scale
                 )
-            sensorLocation.scale = scale
             sensorLocation
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
index dc338d0..c08756f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractor.kt
@@ -91,6 +91,7 @@
         challenge: Long,
         opPackageName: String,
         onSwitchToCredential: Boolean,
+        isLandscape: Boolean,
     )
 
     /** Unset the current authentication request. */
@@ -102,6 +103,7 @@
 @Inject
 constructor(
     fingerprintPropertyRepository: FingerprintPropertyRepository,
+    private val displayStateInteractor: DisplayStateInteractor,
     private val promptRepository: PromptRepository,
     private val lockPatternUtils: LockPatternUtils,
 ) : PromptSelectorInteractor {
@@ -166,7 +168,9 @@
             modalities,
             promptRepository.challenge.value!!,
             promptRepository.opPackageName.value!!,
-            true /*onSwitchToCredential*/
+            onSwitchToCredential = true,
+            // isLandscape value is not important when onSwitchToCredential is true
+            isLandscape = false,
         )
     }
 
@@ -178,6 +182,7 @@
         challenge: Long,
         opPackageName: String,
         onSwitchToCredential: Boolean,
+        isLandscape: Boolean,
     ) {
         val hasCredentialViewShown = promptKind.value.isCredential()
         val showBpForCredential =
@@ -189,11 +194,30 @@
                 !promptInfo.isContentViewMoreOptionsButtonUsed
         val showBpWithoutIconForCredential = showBpForCredential && !hasCredentialViewShown
         var kind: PromptKind = PromptKind.None
+
         if (onSwitchToCredential) {
             kind = getCredentialType(lockPatternUtils, effectiveUserId)
         } else if (Utils.isBiometricAllowed(promptInfo) || showBpWithoutIconForCredential) {
-            // TODO(b/330908557): check to show one pane or two pane
-            kind = PromptKind.Biometric(modalities)
+            // TODO(b/330908557): Subscribe to
+            // displayStateInteractor.currentRotation.value.isDefaultOrientation() for checking
+            // `isLandscape` after removing AuthContinerView.
+            kind =
+                if (isLandscape) {
+                    val paneType =
+                        when {
+                            displayStateInteractor.isLargeScreen.value ->
+                                PromptKind.Biometric.PaneType.ONE_PANE_LARGE_SCREEN_LANDSCAPE
+                            showBpWithoutIconForCredential ->
+                                PromptKind.Biometric.PaneType.ONE_PANE_NO_SENSOR_LANDSCAPE
+                            else -> PromptKind.Biometric.PaneType.TWO_PANE_LANDSCAPE
+                        }
+                    PromptKind.Biometric(
+                        modalities,
+                        paneType = paneType,
+                    )
+                } else {
+                    PromptKind.Biometric(modalities)
+                }
         } else if (isDeviceCredentialAllowed(promptInfo)) {
             kind = getCredentialType(lockPatternUtils, effectiveUserId)
         }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/SensorLocation.kt b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/SensorLocation.kt
index dddadbd..2f2f3a3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/SensorLocation.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/SensorLocation.kt
@@ -16,18 +16,18 @@
 
 package com.android.systemui.biometrics.shared.model
 
-/** Provides current sensor location information in the current screen resolution [scale]. */
+/**
+ * Provides current sensor location information in the current screen resolution [scale].
+ *
+ * @property scale Scale to apply to the sensor location's natural parameters to support different
+ *   screen resolutions.
+ */
 data class SensorLocation(
     private val naturalCenterX: Int,
     private val naturalCenterY: Int,
-    private val naturalRadius: Int
+    private val naturalRadius: Int,
+    private val scale: Float = 1f
 ) {
-    /**
-     * Scale to apply to the sensor location's natural parameters to support different screen
-     * resolutions.
-     */
-    var scale: Float = 1f
-
     val centerX: Float
         get() {
             return naturalCenterX * scale
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index 47174c0..c836f89 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -93,6 +93,7 @@
 
         if (constraintBp()) {
             val leftGuideline = view.requireViewById<Guideline>(R.id.leftGuideline)
+            val topGuideline = view.requireViewById<Guideline>(R.id.topGuideline)
             val rightGuideline = view.requireViewById<Guideline>(R.id.rightGuideline)
             val midGuideline = view.findViewById<Guideline>(R.id.midGuideline)
 
@@ -355,6 +356,18 @@
                                 )
                             }
 
+                            if (bounds.top >= 0) {
+                                mediumConstraintSet.setGuidelineBegin(topGuideline.id, bounds.top)
+                                smallConstraintSet.setGuidelineBegin(topGuideline.id, bounds.top)
+                            } else if (bounds.top < 0) {
+                                mediumConstraintSet.setGuidelineEnd(
+                                    topGuideline.id,
+                                    abs(bounds.top)
+                                )
+                                smallConstraintSet.setGuidelineEnd(topGuideline.id, abs(bounds.top))
+                            }
+
+                            // Use rect bottom to set mid guideline of two-pane.
                             if (midGuideline != null) {
                                 if (bounds.bottom >= 0) {
                                     midGuideline.setGuidelineEnd(bounds.bottom)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
index fcc6992..adf5b74 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt
@@ -17,11 +17,17 @@
 
 package com.android.systemui.biometrics.ui.binder
 
+import android.graphics.drawable.Animatable2
 import android.graphics.drawable.AnimatedVectorDrawable
+import android.graphics.drawable.Drawable
+import android.util.Log
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.airbnb.lottie.LottieAnimationView
 import com.airbnb.lottie.LottieOnCompositionLoadedListener
+import com.airbnb.lottie.LottieListener
 import com.android.settingslib.widget.LottieColorUtils
 import com.android.systemui.Flags.constraintBp
 import com.android.systemui.biometrics.ui.viewmodel.PromptIconViewModel
@@ -30,11 +36,14 @@
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
 import com.android.systemui.util.kotlin.Utils.Companion.toQuad
+import com.android.systemui.util.kotlin.Utils.Companion.toQuint
 import com.android.systemui.util.kotlin.Utils.Companion.toTriple
 import com.android.systemui.util.kotlin.sample
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.launch
 
+private const val TAG = "PromptIconViewBinder"
+
 /** Sub-binder for [BiometricPromptLayout.iconView]. */
 object PromptIconViewBinder {
     /**
@@ -61,6 +70,16 @@
                 }
 
                 var faceIcon: AnimatedVectorDrawable? = null
+                val faceIconCallback =
+                    object : Animatable2.AnimationCallback() {
+                        override fun onAnimationStart(drawable: Drawable) {
+                            viewModel.onAnimationStart()
+                        }
+
+                        override fun onAnimationEnd(drawable: Drawable) {
+                            viewModel.onAnimationEnd()
+                        }
+                    }
 
                 if (!constraintBp()) {
                     launch {
@@ -126,17 +145,24 @@
                             combine(
                                 viewModel.activeAuthType,
                                 viewModel.shouldAnimateIconView,
+                                viewModel.shouldRepeatAnimation,
                                 viewModel.showingError,
-                                ::Triple
+                                ::toQuad
                             ),
-                            ::toQuad
+                            ::toQuint
                         )
-                        .collect { (iconAsset, activeAuthType, shouldAnimateIconView, showingError)
-                            ->
+                        .collect {
+                            (
+                                iconAsset,
+                                activeAuthType,
+                                shouldAnimateIconView,
+                                shouldRepeatAnimation,
+                                showingError) ->
                             if (iconAsset != -1) {
                                 when (activeAuthType) {
                                     AuthType.Fingerprint,
                                     AuthType.Coex -> {
+                                        iconView.setIconFailureListener(iconAsset, activeAuthType)
                                         iconView.setAnimation(iconAsset)
                                         iconView.frame = 0
 
@@ -145,27 +171,25 @@
                                         }
                                     }
                                     AuthType.Face -> {
-                                        // TODO(b/318569643): Consolidate logic once all face auth
-                                        // assets are migrated from drawable to json
-                                        if (iconAsset == R.raw.face_dialog_authenticating) {
-                                            iconView.setAnimation(iconAsset)
-                                            iconView.frame = 0
-
+                                        faceIcon?.apply {
+                                            unregisterAnimationCallback(faceIconCallback)
+                                            stop()
+                                        }
+                                        faceIcon =
+                                            iconView.context.getDrawable(iconAsset)
+                                                as AnimatedVectorDrawable
+                                        faceIcon?.apply {
+                                            iconView.setIconFailureListener(
+                                                iconAsset,
+                                                activeAuthType
+                                            )
+                                            iconView.setImageDrawable(this)
                                             if (shouldAnimateIconView) {
-                                                iconView.playAnimation()
-                                                iconView.loop(true)
-                                            }
-                                        } else {
-                                            faceIcon?.apply { stop() }
-                                            faceIcon =
-                                                iconView.context.getDrawable(iconAsset)
-                                                    as AnimatedVectorDrawable
-                                            faceIcon?.apply {
-                                                iconView.setImageDrawable(this)
-                                                if (shouldAnimateIconView) {
-                                                    forceAnimationOnUI()
-                                                    start()
+                                                forceAnimationOnUI()
+                                                if (shouldRepeatAnimation) {
+                                                    registerAnimationCallback(faceIconCallback)
                                                 }
+                                                start()
                                             }
                                         }
                                     }
@@ -188,6 +212,7 @@
                         )
                         .collect { (iconOverlayAsset, shouldAnimateIconOverlay, showingError) ->
                             if (iconOverlayAsset != -1) {
+                                iconOverlayView.setIconOverlayFailureListener(iconOverlayAsset)
                                 iconOverlayView.setAnimation(iconOverlayAsset)
                                 iconOverlayView.frame = 0
                                 LottieColorUtils.applyDynamicColors(
@@ -222,3 +247,96 @@
         }
     }
 }
+
+private val assetIdToString: Map<Int, String> =
+    mapOf(
+        // UDFPS assets
+        R.raw.fingerprint_dialogue_error_to_fingerprint_lottie to
+            "fingerprint_dialogue_error_to_fingerprint_lottie",
+        R.raw.fingerprint_dialogue_error_to_success_lottie to
+            "fingerprint_dialogue_error_to_success_lottie",
+        R.raw.fingerprint_dialogue_fingerprint_to_error_lottie to
+            "fingerprint_dialogue_fingerprint_to_error_lottie",
+        R.raw.fingerprint_dialogue_fingerprint_to_success_lottie to
+            "fingerprint_dialogue_fingerprint_to_success_lottie",
+        // SFPS assets
+        R.raw.biometricprompt_fingerprint_to_error_landscape to
+            "biometricprompt_fingerprint_to_error_landscape",
+        R.raw.biometricprompt_folded_base_bottomright to "biometricprompt_folded_base_bottomright",
+        R.raw.biometricprompt_folded_base_default to "biometricprompt_folded_base_default",
+        R.raw.biometricprompt_folded_base_topleft to "biometricprompt_folded_base_topleft",
+        R.raw.biometricprompt_landscape_base to "biometricprompt_landscape_base",
+        R.raw.biometricprompt_portrait_base_bottomright to
+            "biometricprompt_portrait_base_bottomright",
+        R.raw.biometricprompt_portrait_base_topleft to "biometricprompt_portrait_base_topleft",
+        R.raw.biometricprompt_symbol_error_to_fingerprint_landscape to
+            "biometricprompt_symbol_error_to_fingerprint_landscape",
+        R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_bottomright to
+            "biometricprompt_symbol_error_to_fingerprint_portrait_bottomright",
+        R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_topleft to
+            "biometricprompt_symbol_error_to_fingerprint_portrait_topleft",
+        R.raw.biometricprompt_symbol_error_to_success_landscape to
+            "biometricprompt_symbol_error_to_success_landscape",
+        R.raw.biometricprompt_symbol_error_to_success_portrait_bottomright to
+            "biometricprompt_symbol_error_to_success_portrait_bottomright",
+        R.raw.biometricprompt_symbol_error_to_success_portrait_topleft to
+            "biometricprompt_symbol_error_to_success_portrait_topleft",
+        R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright to
+            "biometricprompt_symbol_fingerprint_to_error_portrait_bottomright",
+        R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft to
+            "biometricprompt_symbol_fingerprint_to_error_portrait_topleft",
+        R.raw.biometricprompt_symbol_fingerprint_to_success_landscape to
+            "biometricprompt_symbol_fingerprint_to_success_landscape",
+        R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_bottomright to
+            "biometricprompt_symbol_fingerprint_to_success_portrait_bottomright",
+        R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_topleft to
+            "biometricprompt_symbol_fingerprint_to_success_portrait_topleft",
+        // Face assets
+        R.drawable.face_dialog_wink_from_dark to "face_dialog_wink_from_dark",
+        R.drawable.face_dialog_dark_to_checkmark to "face_dialog_dark_to_checkmark",
+        R.drawable.face_dialog_pulse_light_to_dark to "face_dialog_pulse_light_to_dark",
+        R.drawable.face_dialog_pulse_dark_to_light to "face_dialog_pulse_dark_to_light",
+        R.drawable.face_dialog_dark_to_error to "face_dialog_dark_to_error",
+        R.drawable.face_dialog_error_to_idle to "face_dialog_error_to_idle",
+        R.drawable.face_dialog_idle_static to "face_dialog_idle_static",
+        // Co-ex assets
+        R.raw.fingerprint_dialogue_unlocked_to_checkmark_success_lottie to
+            "fingerprint_dialogue_unlocked_to_checkmark_success_lottie",
+        R.raw.fingerprint_dialogue_error_to_unlock_lottie to
+            "fingerprint_dialogue_error_to_unlock_lottie",
+        R.raw.fingerprint_dialogue_fingerprint_to_unlock_lottie to
+            "fingerprint_dialogue_fingerprint_to_unlock_lottie",
+    )
+
+private fun getAssetNameFromId(id: Int): String {
+    return assetIdToString.getOrDefault(id, "Asset $id not found")
+}
+
+private fun LottieAnimationView.setIconFailureListener(iconAsset: Int, activeAuthType: AuthType) {
+    setFailureListener(
+        LottieListener<Throwable> { result: Throwable? ->
+            Log.d(
+                TAG,
+                "Collecting iconAsset | " +
+                    "activeAuthType = $activeAuthType | " +
+                    "Invalid resource id: $iconAsset, " +
+                    "name ${getAssetNameFromId(iconAsset)}",
+                result
+            )
+        }
+    )
+}
+
+private fun LottieAnimationView.setIconOverlayFailureListener(iconOverlayAsset: Int) {
+    setFailureListener(
+        LottieListener<Throwable> { result: Throwable? ->
+            Log.d(
+                TAG,
+                "Collecting iconOverlayAsset | " +
+                    "Invalid resource id: $iconOverlayAsset, " +
+                    "name ${getAssetNameFromId(iconOverlayAsset)}",
+                result
+            )
+        }
+    )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
index 901d751..bde3e99 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt
@@ -21,6 +21,7 @@
 import android.annotation.RawRes
 import android.content.res.Configuration
 import android.graphics.Rect
+import android.hardware.face.Face
 import android.util.RotationUtils
 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
@@ -31,10 +32,12 @@
 import com.android.systemui.util.kotlin.combine
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
 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
 
 /**
  * Models UI of [BiometricPromptLayout.iconView] and [BiometricPromptLayout.biometric_icon_overlay]
@@ -55,8 +58,11 @@
     }
 
     /**
-     * Indicates what auth type the UI currently displays. Fingerprint-only auth -> Fingerprint
-     * Face-only auth -> Face Co-ex auth, implicit flow -> Face Co-ex auth, explicit flow -> Coex
+     * Indicates what auth type the UI currently displays.
+     * Fingerprint-only auth -> Fingerprint
+     * Face-only auth -> Face
+     * Co-ex auth, implicit flow -> Face
+     * Co-ex auth, explicit flow -> Coex
      */
     val activeAuthType: Flow<AuthType> =
         combine(
@@ -113,6 +119,35 @@
         _previousIconOverlayWasError.value = previousIconOverlayWasError
     }
 
+    /** Called when iconView begins animating. */
+    fun onAnimationStart() {
+        _animationEnded.value = false
+    }
+
+    /** Called when iconView ends animating. */
+    fun onAnimationEnd() {
+        _animationEnded.value = true
+    }
+
+    private val _animationEnded: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
+    /**
+     * Whether a face iconView should pulse (i.e. while isAuthenticating and previous animation
+     * ended).
+     */
+    val shouldPulseAnimation: Flow<Boolean> =
+        combine(_animationEnded, promptViewModel.isAuthenticating) {
+                animationEnded,
+                isAuthenticating ->
+                animationEnded && isAuthenticating
+            }
+            .distinctUntilChanged()
+
+    private val _lastPulseLightToDark: MutableStateFlow<Boolean> = MutableStateFlow(false)
+
+    /** Tracks whether a face iconView last pulsed light to dark (vs. dark to light) */
+    val lastPulseLightToDark: Flow<Boolean> = _lastPulseLightToDark.asStateFlow()
+
     val iconSize: Flow<Pair<Int, Int>> =
         combine(
             promptViewModel.position,
@@ -160,22 +195,35 @@
                         }
                     }
                 AuthType.Face ->
-                    combine(
-                        promptViewModel.isAuthenticated.distinctUntilChanged(),
-                        promptViewModel.isAuthenticating.distinctUntilChanged(),
-                        promptViewModel.isPendingConfirmation.distinctUntilChanged(),
-                        promptViewModel.showingError.distinctUntilChanged()
-                    ) {
-                        authState: PromptAuthState,
-                        isAuthenticating: Boolean,
-                        isPendingConfirmation: Boolean,
-                        showingError: Boolean ->
-                        getFaceIconViewAsset(
-                            authState,
-                            isAuthenticating,
-                            isPendingConfirmation,
-                            showingError
-                        )
+                    shouldPulseAnimation.flatMapLatest { shouldPulseAnimation: Boolean ->
+                        if (shouldPulseAnimation) {
+                            val iconAsset =
+                                if (_lastPulseLightToDark.value) {
+                                    R.drawable.face_dialog_pulse_dark_to_light
+                                } else {
+                                    R.drawable.face_dialog_pulse_light_to_dark
+                                }
+                            _lastPulseLightToDark.value = !_lastPulseLightToDark.value
+                            flowOf(iconAsset)
+                        } else {
+                            combine(
+                                promptViewModel.isAuthenticated.distinctUntilChanged(),
+                                promptViewModel.isAuthenticating.distinctUntilChanged(),
+                                promptViewModel.isPendingConfirmation.distinctUntilChanged(),
+                                promptViewModel.showingError.distinctUntilChanged()
+                            ) {
+                                authState: PromptAuthState,
+                                isAuthenticating: Boolean,
+                                isPendingConfirmation: Boolean,
+                                showingError: Boolean ->
+                                getFaceIconViewAsset(
+                                    authState,
+                                    isAuthenticating,
+                                    isPendingConfirmation,
+                                    showingError
+                                )
+                            }
+                        }
                     }
                 AuthType.Coex ->
                     combine(
@@ -279,7 +327,8 @@
         } else if (authState.isAuthenticated) {
             R.drawable.face_dialog_dark_to_checkmark
         } else if (isAuthenticating) {
-            R.raw.face_dialog_authenticating
+            _lastPulseLightToDark.value = false
+            R.drawable.face_dialog_pulse_dark_to_light
         } else if (showingError) {
             R.drawable.face_dialog_dark_to_error
         } else if (_previousIconWasError.value) {
@@ -654,6 +703,16 @@
             }
         }
 
+    /** Whether the current BiometricPromptLayout.iconView asset animation should be repeated. */
+    val shouldRepeatAnimation: Flow<Boolean> =
+        activeAuthType.flatMapLatest { activeAuthType: AuthType ->
+            when (activeAuthType) {
+                AuthType.Fingerprint,
+                AuthType.Coex -> flowOf(false)
+                AuthType.Face -> promptViewModel.isAuthenticating.map { it }
+            }
+        }
+
     /** Called on configuration changes */
     fun onConfigurationChanged(newConfig: Configuration) {
         displayStateInteractor.onConfigurationChanged(newConfig)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index 156ec6b..c17b83d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -261,10 +261,13 @@
         combine(
                 _forceLargeSize,
                 displayStateInteractor.isLargeScreen,
-                displayStateInteractor.currentRotation
+                displayStateInteractor.currentRotation,
             ) { forceLarge, isLargeScreen, rotation ->
                 when {
-                    forceLarge || isLargeScreen -> PromptPosition.Bottom
+                    forceLarge ||
+                        isLargeScreen ||
+                        promptKind.value.isOnePaneNoSensorLandscapeBiometric() ->
+                        PromptPosition.Bottom
                     rotation == DisplayRotation.ROTATION_90 -> PromptPosition.Right
                     rotation == DisplayRotation.ROTATION_270 -> PromptPosition.Left
                     rotation == DisplayRotation.ROTATION_180 -> PromptPosition.Top
@@ -297,23 +300,27 @@
     /** Prompt panel size padding */
     private val smallHorizontalGuidelinePadding =
         context.resources.getDimensionPixelSize(
-            R.dimen.biometric_prompt_small_horizontal_guideline_padding
+            R.dimen.biometric_prompt_land_small_horizontal_guideline_padding
         )
     private val udfpsHorizontalGuidelinePadding =
         context.resources.getDimensionPixelSize(
-            R.dimen.biometric_prompt_udfps_horizontal_guideline_padding
+            R.dimen.biometric_prompt_two_pane_udfps_horizontal_guideline_padding
         )
     private val udfpsMidGuidelinePadding =
         context.resources.getDimensionPixelSize(
-            R.dimen.biometric_prompt_udfps_mid_guideline_padding
+            R.dimen.biometric_prompt_two_pane_udfps_mid_guideline_padding
+        )
+    private val mediumTopGuidelinePadding =
+        context.resources.getDimensionPixelSize(
+            R.dimen.biometric_prompt_one_pane_medium_top_guideline_padding
         )
     private val mediumHorizontalGuidelinePadding =
         context.resources.getDimensionPixelSize(
-            R.dimen.biometric_prompt_medium_horizontal_guideline_padding
+            R.dimen.biometric_prompt_two_pane_medium_horizontal_guideline_padding
         )
     private val mediumMidGuidelinePadding =
         context.resources.getDimensionPixelSize(
-            R.dimen.biometric_prompt_medium_mid_guideline_padding
+            R.dimen.biometric_prompt_two_pane_medium_mid_guideline_padding
         )
 
     /** Rect for positioning biometric icon */
@@ -416,9 +423,9 @@
      * asset to be loaded before determining the prompt size.
      */
     val isIconViewLoaded: Flow<Boolean> =
-        combine(modalities, _isIconViewLoaded.asStateFlow()) { modalities, isIconViewLoaded ->
-                val noIcon = modalities.isEmpty
-                noIcon || isIconViewLoaded
+        combine(hideSensorIcon, _isIconViewLoaded.asStateFlow()) { hideSensorIcon, isIconViewLoaded
+                ->
+                hideSensorIcon || isIconViewLoaded
             }
             .distinctUntilChanged()
 
@@ -448,17 +455,24 @@
      * from opposite side of the screen
      */
     val guidelineBounds: Flow<Rect> =
-        combine(iconPosition, size, position, modalities) { _, size, position, modalities ->
+        combine(iconPosition, promptKind, size, position, modalities) {
+                _,
+                promptKind,
+                size,
+                position,
+                modalities ->
                 when (position) {
-                    PromptPosition.Bottom -> Rect(0, 0, 0, 0)
+                    PromptPosition.Bottom ->
+                        if (promptKind.isOnePaneNoSensorLandscapeBiometric()) {
+                            Rect(0, 0, 0, 0)
+                        } else {
+                            Rect(0, mediumTopGuidelinePadding, 0, 0)
+                        }
                     PromptPosition.Right ->
                         if (size.isSmall) {
                             Rect(-smallHorizontalGuidelinePadding, 0, 0, 0)
                         } else if (modalities.hasUdfps) {
                             Rect(udfpsHorizontalGuidelinePadding, 0, 0, udfpsMidGuidelinePadding)
-                        } else if (modalities.isEmpty) {
-                            // TODO: Temporary fix until no biometric landscape layout is added
-                            Rect(-mediumHorizontalGuidelinePadding, 0, 0, 6)
                         } else {
                             Rect(-mediumHorizontalGuidelinePadding, 0, 0, mediumMidGuidelinePadding)
                         }
@@ -467,9 +481,6 @@
                             Rect(0, 0, -smallHorizontalGuidelinePadding, 0)
                         } else if (modalities.hasUdfps) {
                             Rect(0, 0, udfpsHorizontalGuidelinePadding, -udfpsMidGuidelinePadding)
-                        } else if (modalities.isEmpty) {
-                            // TODO: Temporary fix until no biometric landscape layout is added
-                            Rect(0, 0, -mediumHorizontalGuidelinePadding, -6)
                         } else {
                             Rect(
                                 0,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index 7c41b75..40a141d 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -20,6 +20,8 @@
 import android.app.admin.DevicePolicyResources
 import android.content.Context
 import android.graphics.Bitmap
+import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.input.key.type
 import androidx.core.graphics.drawable.toBitmap
 import com.android.compose.animation.scene.Back
 import com.android.compose.animation.scene.SceneKey
@@ -326,7 +328,8 @@
                 { message },
                 failedAttempts,
                 remainingAttempts,
-            ) ?: message
+            )
+                ?: message
         } else {
             message
         }
@@ -343,7 +346,8 @@
                     .KEYGUARD_DIALOG_FAILED_ATTEMPTS_ERASING_PROFILE,
                 { message },
                 failedAttempts,
-            ) ?: message
+            )
+                ?: message
         } else {
             message
         }
@@ -377,6 +381,19 @@
             Swipe(SwipeDirection.Down) to UserActionResult(prevScene),
         )
 
+    /**
+     * Notifies that a key event has occurred.
+     *
+     * @return `true` when the [KeyEvent] was consumed as user input on bouncer; `false` otherwise.
+     */
+    fun onKeyEvent(keyEvent: KeyEvent): Boolean {
+        return (authMethodViewModel.value as? PinBouncerViewModel)?.onKeyEvent(
+            keyEvent.type,
+            keyEvent.nativeKeyEvent.keyCode
+        )
+            ?: false
+    }
+
     data class DialogViewModel(
         val text: String,
 
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
index 4c2380c..aa447ff 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
@@ -19,6 +19,14 @@
 package com.android.systemui.bouncer.ui.viewmodel
 
 import android.content.Context
+import android.view.KeyEvent.KEYCODE_0
+import android.view.KeyEvent.KEYCODE_9
+import android.view.KeyEvent.KEYCODE_DEL
+import android.view.KeyEvent.KEYCODE_NUMPAD_0
+import android.view.KeyEvent.KEYCODE_NUMPAD_9
+import android.view.KeyEvent.isConfirmKey
+import androidx.compose.ui.input.key.KeyEvent
+import androidx.compose.ui.input.key.KeyEventType
 import com.android.keyguard.PinShapeAdapter
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
@@ -196,6 +204,44 @@
             else -> ActionButtonAppearance.Shown
         }
     }
+
+    /**
+     * Notifies that a key event has occurred.
+     *
+     * @return `true` when the [KeyEvent] was consumed as user input on bouncer; `false` otherwise.
+     */
+    fun onKeyEvent(type: KeyEventType, keyCode: Int): Boolean {
+        return when (type) {
+            KeyEventType.KeyUp -> {
+                if (isConfirmKey(keyCode)) {
+                    onAuthenticateButtonClicked()
+                    true
+                } else {
+                    false
+                }
+            }
+            KeyEventType.KeyDown -> {
+                when (keyCode) {
+                    KEYCODE_DEL -> {
+                        onBackspaceButtonClicked()
+                        true
+                    }
+                    in KEYCODE_0..KEYCODE_9 -> {
+                        onPinButtonClicked(keyCode - KEYCODE_0)
+                        true
+                    }
+                    in KEYCODE_NUMPAD_0..KEYCODE_NUMPAD_9 -> {
+                        onPinButtonClicked(keyCode - KEYCODE_NUMPAD_0)
+                        true
+                    }
+                    else -> {
+                        false
+                    }
+                }
+            }
+            else -> false
+        }
+    }
 }
 
 /** Appearance of pin-pad action buttons. */
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/dagger/ScreenBrightnessModule.kt b/packages/SystemUI/src/com/android/systemui/brightness/dagger/ScreenBrightnessModule.kt
index 2b9fc73..7a9429e 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/dagger/ScreenBrightnessModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/dagger/ScreenBrightnessModule.kt
@@ -20,8 +20,15 @@
 import com.android.systemui.brightness.data.repository.BrightnessPolicyRepositoryImpl
 import com.android.systemui.brightness.data.repository.ScreenBrightnessDisplayManagerRepository
 import com.android.systemui.brightness.data.repository.ScreenBrightnessRepository
+import com.android.systemui.brightness.shared.model.BrightnessLog
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.TableLogBufferFactory
 import dagger.Binds
 import dagger.Module
+import dagger.Provides
 
 @Module
 interface ScreenBrightnessModule {
@@ -33,4 +40,20 @@
 
     @Binds
     fun bindPolicyRepository(impl: BrightnessPolicyRepositoryImpl): BrightnessPolicyRepository
+
+    companion object {
+        @Provides
+        @SysUISingleton
+        @BrightnessLog
+        fun providesBrightnessTableLog(factory: TableLogBufferFactory): TableLogBuffer {
+            return factory.create("BrightnessTableLog", 50)
+        }
+
+        @Provides
+        @SysUISingleton
+        @BrightnessLog
+        fun providesBrightnessLog(factory: LogBufferFactory): LogBuffer {
+            return factory.create("BrightnessLog", 50)
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/data/model/LinearBrightness.kt b/packages/SystemUI/src/com/android/systemui/brightness/data/model/LinearBrightness.kt
deleted file mode 100644
index 608f301..0000000
--- a/packages/SystemUI/src/com/android/systemui/brightness/data/model/LinearBrightness.kt
+++ /dev/null
@@ -1,30 +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.brightness.data.model
-
-@JvmInline
-value class LinearBrightness(val floatValue: Float) {
-    fun clamp(min: LinearBrightness, max: LinearBrightness): LinearBrightness {
-        return if (floatValue < min.floatValue) {
-            min
-        } else if (floatValue > max.floatValue) {
-            max
-        } else {
-            this
-        }
-    }
-}
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 9ed11d1..37d1887 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
@@ -19,12 +19,18 @@
 import android.annotation.SuppressLint
 import android.hardware.display.BrightnessInfo
 import android.hardware.display.DisplayManager
-import com.android.systemui.brightness.data.model.LinearBrightness
+import com.android.systemui.brightness.shared.model.BrightnessLog
+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.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.DisplayId
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.table.TableLogBuffer
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
@@ -32,13 +38,13 @@
 import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
 import kotlinx.coroutines.channels.awaitClose
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.shareIn
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
@@ -78,6 +84,8 @@
 constructor(
     @DisplayId private val displayId: Int,
     private val displayManager: DisplayManager,
+    @BrightnessLog private val logBuffer: LogBuffer,
+    @BrightnessLog private val tableBuffer: TableLogBuffer,
     @Application private val applicationScope: CoroutineScope,
     @Background private val backgroundContext: CoroutineContext,
 ) : ScreenBrightnessRepository {
@@ -100,6 +108,7 @@
                         displayManager.setBrightness(displayId, value)
                     }
                 }
+                logBrightnessChange(call is SetBrightnessMethod.Permanent, value)
             }
         }
     }
@@ -147,13 +156,15 @@
         brightnessInfo
             .filterNotNull()
             .map { LinearBrightness(it.brightnessMinimum) }
-            .shareIn(applicationScope, SharingStarted.WhileSubscribed())
+            .logDiffForTable(tableBuffer, TABLE_PREFIX_LINEAR, TABLE_COLUMN_MIN, null)
+            .stateIn(applicationScope, SharingStarted.WhileSubscribed(), LinearBrightness(0f))
 
-    override val maxLinearBrightness =
+    override val maxLinearBrightness: SharedFlow<LinearBrightness> =
         brightnessInfo
             .filterNotNull()
             .map { LinearBrightness(it.brightnessMaximum) }
-            .shareIn(applicationScope, SharingStarted.WhileSubscribed())
+            .logDiffForTable(tableBuffer, TABLE_PREFIX_LINEAR, TABLE_COLUMN_MAX, null)
+            .stateIn(applicationScope, SharingStarted.WhileSubscribed(), LinearBrightness(1f))
 
     override suspend fun getMinMaxLinearBrightness(): Pair<LinearBrightness, LinearBrightness> {
         val brightnessInfo = brightnessInfo.value ?: brightnessInfoValue()
@@ -166,7 +177,8 @@
         brightnessInfo
             .filterNotNull()
             .map { LinearBrightness(it.brightness) }
-            .shareIn(applicationScope, SharingStarted.WhileSubscribed())
+            .logDiffForTable(tableBuffer, TABLE_PREFIX_LINEAR, TABLE_COLUMN_BRIGHTNESS, null)
+            .stateIn(applicationScope, SharingStarted.WhileSubscribed(), LinearBrightness(0f))
 
     override fun setTemporaryBrightness(value: LinearBrightness) {
         apiQueue.trySend(SetBrightnessMethod.Temporary(value))
@@ -183,4 +195,21 @@
         @JvmInline
         value class Permanent(override val value: LinearBrightness) : SetBrightnessMethod
     }
+
+    private fun logBrightnessChange(permanent: Boolean, value: Float) {
+        logBuffer.log(
+            LOG_BUFFER_BRIGHTNESS_CHANGE_TAG,
+            if (permanent) LogLevel.DEBUG else LogLevel.VERBOSE,
+            { str1 = value.formatBrightness() },
+            { "Change requested: $str1" }
+        )
+    }
+
+    private companion object {
+        const val TABLE_COLUMN_BRIGHTNESS = "brightness"
+        const val TABLE_COLUMN_MIN = "min"
+        const val TABLE_COLUMN_MAX = "max"
+        const val TABLE_PREFIX_LINEAR = "linear"
+        const val LOG_BUFFER_BRIGHTNESS_CHANGE_TAG = "BrightnessChange"
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractor.kt b/packages/SystemUI/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractor.kt
index 799a0a1..5647f521 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractor.kt
@@ -17,12 +17,20 @@
 package com.android.systemui.brightness.domain.interactor
 
 import com.android.settingslib.display.BrightnessUtils
-import com.android.systemui.brightness.data.model.LinearBrightness
 import com.android.systemui.brightness.data.repository.ScreenBrightnessRepository
-import com.android.systemui.brightness.shared.GammaBrightness
+import com.android.systemui.brightness.shared.model.BrightnessLog
+import com.android.systemui.brightness.shared.model.GammaBrightness
+import com.android.systemui.brightness.shared.model.LinearBrightness
+import com.android.systemui.brightness.shared.model.logDiffForTable
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.log.table.TableLogBuffer
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
 
 /**
  * Converts between [GammaBrightness] and [LinearBrightness].
@@ -34,6 +42,8 @@
 @Inject
 constructor(
     private val screenBrightnessRepository: ScreenBrightnessRepository,
+    @Application private val applicationScope: CoroutineScope,
+    @BrightnessLog private val tableBuffer: TableLogBuffer,
 ) {
     /** Maximum value in the Gamma space for brightness */
     val maxGammaBrightness = GammaBrightness(BrightnessUtils.GAMMA_SPACE_MAX)
@@ -45,15 +55,17 @@
      * Brightness in the Gamma space for the current display. It will always represent a value
      * between [minGammaBrightness] and [maxGammaBrightness]
      */
-    val gammaBrightness =
+    val gammaBrightness: Flow<GammaBrightness> =
         with(screenBrightnessRepository) {
             combine(
-                linearBrightness,
-                minLinearBrightness,
-                maxLinearBrightness,
-            ) { brightness, min, max ->
-                brightness.toGammaBrightness(min, max)
-            }
+                    linearBrightness,
+                    minLinearBrightness,
+                    maxLinearBrightness,
+                ) { brightness, min, max ->
+                    brightness.toGammaBrightness(min, max)
+                }
+                .logDiffForTable(tableBuffer, TABLE_PREFIX_GAMMA, TABLE_COLUMN_BRIGHTNESS, null)
+                .stateIn(applicationScope, SharingStarted.WhileSubscribed(), GammaBrightness(0))
         }
 
     /** Sets the brightness temporarily, while the user is changing it. */
@@ -91,4 +103,9 @@
             BrightnessUtils.convertLinearToGammaFloat(floatValue, min.floatValue, max.floatValue)
         )
     }
+
+    private companion object {
+        const val TABLE_COLUMN_BRIGHTNESS = "brightness"
+        const val TABLE_PREFIX_GAMMA = "gamma"
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/shared/GammaBrightness.kt b/packages/SystemUI/src/com/android/systemui/brightness/shared/GammaBrightness.kt
deleted file mode 100644
index e20d003..0000000
--- a/packages/SystemUI/src/com/android/systemui/brightness/shared/GammaBrightness.kt
+++ /dev/null
@@ -1,29 +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.brightness.shared
-
-import androidx.annotation.IntRange
-import com.android.settingslib.display.BrightnessUtils
-
-@JvmInline
-value class GammaBrightness(
-    @IntRange(
-        from = BrightnessUtils.GAMMA_SPACE_MIN.toLong(),
-        to = BrightnessUtils.GAMMA_SPACE_MAX.toLong()
-    )
-    val value: Int
-)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt b/packages/SystemUI/src/com/android/systemui/brightness/shared/model/BrightnessLog.kt
similarity index 75%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt
rename to packages/SystemUI/src/com/android/systemui/brightness/shared/model/BrightnessLog.kt
index 277dbb7..b514fef 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/shared/model/BrightnessLog.kt
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.brightness.shared.model
 
-import com.android.systemui.kosmos.Kosmos
+import javax.inject.Qualifier
 
-val Kosmos.iconLabelVisibilityRepository by Kosmos.Fixture { IconLabelVisibilityRepository() }
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class BrightnessLog()
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/shared/model/GammaBrightness.kt b/packages/SystemUI/src/com/android/systemui/brightness/shared/model/GammaBrightness.kt
new file mode 100644
index 0000000..7eba626
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/brightness/shared/model/GammaBrightness.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.brightness.shared.model
+
+import androidx.annotation.IntRange
+import com.android.settingslib.display.BrightnessUtils
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.util.kotlin.pairwiseBy
+import kotlinx.coroutines.flow.Flow
+
+@JvmInline
+value class GammaBrightness(
+    @IntRange(
+        from = BrightnessUtils.GAMMA_SPACE_MIN.toLong(),
+        to = BrightnessUtils.GAMMA_SPACE_MAX.toLong()
+    )
+    val value: Int
+)
+
+internal fun Flow<GammaBrightness>.logDiffForTable(
+    tableLogBuffer: TableLogBuffer,
+    columnPrefix: String,
+    columnName: String,
+    initialValue: GammaBrightness?,
+): Flow<GammaBrightness> {
+    val initialValueFun = {
+        tableLogBuffer.logChange(columnPrefix, columnName, initialValue?.value, isInitial = true)
+        initialValue
+    }
+    return this.pairwiseBy(initialValueFun) { prevVal: GammaBrightness?, newVal: GammaBrightness ->
+        if (prevVal != newVal) {
+            tableLogBuffer.logChange(columnPrefix, columnName, newVal.value)
+        }
+        newVal
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/shared/model/LinearBrightness.kt b/packages/SystemUI/src/com/android/systemui/brightness/shared/model/LinearBrightness.kt
new file mode 100644
index 0000000..1c886e6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/brightness/shared/model/LinearBrightness.kt
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.brightness.shared.model
+
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.util.kotlin.pairwiseBy
+import kotlinx.coroutines.flow.Flow
+
+@JvmInline
+value class LinearBrightness(val floatValue: Float) {
+    fun clamp(min: LinearBrightness, max: LinearBrightness): LinearBrightness {
+        return if (floatValue < min.floatValue) {
+            min
+        } else if (floatValue > max.floatValue) {
+            max
+        } else {
+            this
+        }
+    }
+
+    val loggableString: String
+        get() = floatValue.formatBrightness()
+}
+
+fun Float.formatBrightness(): String {
+    return "%.3f".format(this)
+}
+
+internal fun Flow<LinearBrightness>.logDiffForTable(
+    tableLogBuffer: TableLogBuffer,
+    columnPrefix: String,
+    columnName: String,
+    initialValue: LinearBrightness?,
+): Flow<LinearBrightness> {
+    val initialValueFun = {
+        tableLogBuffer.logChange(
+            columnPrefix,
+            columnName,
+            initialValue?.loggableString,
+            isInitial = true
+        )
+        initialValue
+    }
+    return this.pairwiseBy(initialValueFun) { prevVal: LinearBrightness?, newVal: LinearBrightness
+        ->
+        if (prevVal != newVal) {
+            tableLogBuffer.logChange(columnPrefix, columnName, newVal.loggableString)
+        }
+        newVal
+    }
+}
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 a51d8ff..f991d5b 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
@@ -33,14 +33,13 @@
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.PlatformSlider
-import com.android.systemui.brightness.shared.GammaBrightness
+import com.android.systemui.brightness.shared.model.GammaBrightness
 import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel
 import com.android.systemui.brightness.ui.viewmodel.Drag
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.common.ui.compose.Icon
 import com.android.systemui.utils.PolicyRestriction
-import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
 
 @Composable
@@ -107,8 +106,8 @@
     viewModel: BrightnessSliderViewModel,
     modifier: Modifier = Modifier,
 ) {
-    val gamma: Int by
-        viewModel.currentBrightness.map { it.value }.collectAsStateWithLifecycle(initialValue = 0)
+    val state by viewModel.currentBrightness.collectAsStateWithLifecycle()
+    val gamma = state.value
     val coroutineScope = rememberCoroutineScope()
     val restriction by
         viewModel.policyRestriction.collectAsStateWithLifecycle(
diff --git a/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt
index f0988ba..16a1dcc 100644
--- a/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModel.kt
@@ -18,14 +18,18 @@
 
 import com.android.systemui.brightness.domain.interactor.BrightnessPolicyEnforcementInteractor
 import com.android.systemui.brightness.domain.interactor.ScreenBrightnessInteractor
-import com.android.systemui.brightness.shared.GammaBrightness
+import com.android.systemui.brightness.shared.model.GammaBrightness
 import com.android.systemui.common.shared.model.ContentDescription
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.common.shared.model.Text
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.res.R
 import com.android.systemui.utils.PolicyRestriction
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.stateIn
 
 @SysUISingleton
 class BrightnessSliderViewModel
@@ -33,8 +37,14 @@
 constructor(
     private val screenBrightnessInteractor: ScreenBrightnessInteractor,
     private val brightnessPolicyEnforcementInteractor: BrightnessPolicyEnforcementInteractor,
+    @Application private val applicationScope: CoroutineScope,
 ) {
-    val currentBrightness = screenBrightnessInteractor.gammaBrightness
+    val currentBrightness =
+        screenBrightnessInteractor.gammaBrightness.stateIn(
+            applicationScope,
+            SharingStarted.WhileSubscribed(),
+            GammaBrightness(0)
+        )
 
     val maxBrightness = screenBrightnessInteractor.maxGammaBrightness
     val minBrightness = screenBrightnessInteractor.minGammaBrightness
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
index 153b7aa..8993a3b 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalDreamStartable.kt
@@ -19,6 +19,7 @@
 import android.annotation.SuppressLint
 import android.app.DreamManager
 import com.android.systemui.CoreStartable
+import com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming
 import com.android.systemui.Flags.communalHub
 import com.android.systemui.Flags.restartDreamOnUnocclude
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
@@ -30,11 +31,11 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.util.kotlin.Utils.Companion.sample
 import com.android.systemui.util.kotlin.sample
-import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
+import javax.inject.Inject
 
 /**
  * A [CoreStartable] responsible for automatically starting the dream when the communal hub is
@@ -78,6 +79,7 @@
                 if (
                     finishedState == KeyguardState.GLANCEABLE_HUB &&
                         !dreaming &&
+                        !glanceableHubAllowKeyguardWhenDreaming() &&
                         dreamManager.canStartDreaming(isAwake)
                 ) {
                     dreamManager.startDream()
diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
index 6f20a8d..d522c7e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt
@@ -20,6 +20,7 @@
 import com.android.compose.animation.scene.SceneKey
 import com.android.systemui.CoreStartable
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.communal.shared.model.CommunalTransitionKeys
 import com.android.systemui.dagger.SysUISingleton
@@ -32,10 +33,13 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.statusbar.NotificationShadeWindowController
+import com.android.systemui.statusbar.phone.CentralSurfaces
 import com.android.systemui.util.kotlin.emitOnStart
+import com.android.systemui.util.kotlin.getValue
 import com.android.systemui.util.kotlin.sample
 import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
 import com.android.systemui.util.settings.SystemSettings
+import java.util.Optional
 import javax.inject.Inject
 import kotlin.time.Duration.Companion.milliseconds
 import kotlin.time.Duration.Companion.seconds
@@ -64,9 +68,11 @@
 constructor(
     private val dockManager: DockManager,
     private val communalInteractor: CommunalInteractor,
+    private val communalSceneInteractor: CommunalSceneInteractor,
     private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
     private val keyguardInteractor: KeyguardInteractor,
     private val systemSettings: SystemSettings,
+    centralSurfacesOpt: Optional<CentralSurfaces>,
     private val notificationShadeWindowController: NotificationShadeWindowController,
     @Application private val applicationScope: CoroutineScope,
     @Background private val bgScope: CoroutineScope,
@@ -78,13 +84,15 @@
 
     private var isDreaming: Boolean = false
 
+    private val centralSurfaces: CentralSurfaces? by centralSurfacesOpt
+
     override fun start() {
         // Handle automatically switching based on keyguard state.
         keyguardTransitionInteractor.startedKeyguardTransitionStep
             .mapLatest(::determineSceneAfterTransition)
             .filterNotNull()
             .onEach { nextScene ->
-                communalInteractor.changeScene(nextScene, CommunalTransitionKeys.SimpleFade)
+                communalSceneInteractor.changeScene(nextScene, CommunalTransitionKeys.SimpleFade)
             }
             .launchIn(applicationScope)
 
@@ -124,7 +132,7 @@
         // app is updated by the Play store, a new timeout should be started.
         bgScope.launch {
             combine(
-                    communalInteractor.desiredScene,
+                    communalSceneInteractor.currentScene,
                     // Emit a value on start so the combine starts.
                     communalInteractor.userActivity.emitOnStart()
                 ) { scene, _ ->
@@ -140,19 +148,19 @@
         }
         bgScope.launch {
             keyguardInteractor.isDreaming
-                .sample(communalInteractor.desiredScene, ::Pair)
+                .sample(communalSceneInteractor.currentScene, ::Pair)
                 .collectLatest { (isDreaming, scene) ->
                     this@CommunalSceneStartable.isDreaming = isDreaming
                     if (scene == CommunalScenes.Communal && isDreaming && timeoutJob == null) {
                         // If dreaming starts after timeout has expired, ex. if dream restarts under
                         // the hub, just close the hub immediately.
-                        communalInteractor.changeScene(CommunalScenes.Blank)
+                        communalSceneInteractor.changeScene(CommunalScenes.Blank)
                     }
                 }
         }
 
         bgScope.launch {
-            communalInteractor.isIdleOnCommunal.collectLatest {
+            communalSceneInteractor.isIdleOnCommunal.collectLatest {
                 withContext(mainDispatcher) {
                     notificationShadeWindowController.setGlanceableHubShowing(it)
                 }
@@ -171,7 +179,7 @@
                 bgScope.launch {
                     delay(screenTimeout.milliseconds)
                     if (isDreaming) {
-                        communalInteractor.changeScene(CommunalScenes.Blank)
+                        communalSceneInteractor.changeScene(CommunalScenes.Blank)
                     }
                     timeoutJob = null
                 }
@@ -184,11 +192,15 @@
         val to = lastStartedTransition.to
         val from = lastStartedTransition.from
         val docked = dockManager.isDocked
+        val launchingActivityOverLockscreen =
+            centralSurfaces?.isLaunchingActivityOverLockscreen ?: false
 
         return when {
-            to == KeyguardState.OCCLUDED -> {
+            to == KeyguardState.OCCLUDED && !launchingActivityOverLockscreen -> {
                 // Hide communal when an activity is started on keyguard, to ensure the activity
-                // underneath the hub is shown.
+                // underneath the hub is shown. When launching activities over lockscreen, we only
+                // change scenes once the activity launch animation is finished, so avoid
+                // changing the scene here.
                 CommunalScenes.Blank
             }
             to == KeyguardState.GLANCEABLE_HUB && from == KeyguardState.OCCLUDED -> {
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt
index d6d08b4..260dcba 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.communal.dagger.Communal
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.scene.shared.model.SceneDataSource
 import javax.inject.Inject
@@ -34,6 +35,7 @@
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
 
 /** Encapsulates the state of communal mode. */
 interface CommunalSceneRepository {
@@ -64,6 +66,7 @@
 class CommunalSceneRepositoryImpl
 @Inject
 constructor(
+    @Application private val applicationScope: CoroutineScope,
     @Background backgroundScope: CoroutineScope,
     @Communal private val sceneDataSource: SceneDataSource,
 ) : CommunalSceneRepository {
@@ -82,11 +85,19 @@
             )
 
     override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) {
-        sceneDataSource.changeScene(toScene, transitionKey)
+        applicationScope.launch {
+            // SceneTransitionLayout state updates must be triggered on the thread the STL was
+            // created on.
+            sceneDataSource.changeScene(toScene, transitionKey)
+        }
     }
 
     override fun snapToScene(toScene: SceneKey) {
-        sceneDataSource.snapToScene(toScene)
+        applicationScope.launch {
+            // SceneTransitionLayout state updates must be triggered on the thread the STL was
+            // created on.
+            sceneDataSource.snapToScene(toScene)
+        }
     }
 
     /**
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 88cb64c..1c47e50 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
@@ -30,6 +30,7 @@
 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.shared.model.CommunalBackgroundType
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.flags.FeatureFlagsClassic
@@ -59,6 +60,9 @@
 
     /** Keyguard widgets enabled state by Device Policy Manager for the specified user. */
     fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean>
+
+    /** The type of background to use for the hub. Used to experiment with different backgrounds. */
+    fun getBackground(user: UserInfo): Flow<CommunalBackgroundType>
 }
 
 @SysUISingleton
@@ -126,6 +130,21 @@
             .emitOnStart()
             .map { devicePolicyManager.areKeyguardWidgetsAllowed(user.id) }
 
+    override fun getBackground(user: UserInfo): Flow<CommunalBackgroundType> =
+        secureSettings
+            .observerFlow(userId = user.id, names = arrayOf(GLANCEABLE_HUB_BACKGROUND_SETTING))
+            .emitOnStart()
+            .map {
+                val intType =
+                    secureSettings.getIntForUser(
+                        GLANCEABLE_HUB_BACKGROUND_SETTING,
+                        CommunalBackgroundType.DEFAULT.value,
+                        user.id
+                    )
+                CommunalBackgroundType.entries.find { type -> type.value == intType }
+                    ?: CommunalBackgroundType.DEFAULT
+            }
+
     private fun getEnabledByUser(user: UserInfo): Flow<Boolean> =
         secureSettings
             .observerFlow(userId = user.id, names = arrayOf(Settings.Secure.GLANCEABLE_HUB_ENABLED))
@@ -141,6 +160,7 @@
 
     companion object {
         const val GLANCEABLE_HUB_CONTENT_SETTING = "glanceable_hub_content_setting"
+        const val GLANCEABLE_HUB_BACKGROUND_SETTING = "glanceable_hub_background"
         private const val ENABLED_SETTING_DEFAULT = 1
     }
 }
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 3e5126a..f043d58 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
@@ -21,6 +21,7 @@
 import com.android.systemui.communal.data.model.CommunalEnabledState
 import com.android.systemui.communal.data.model.CommunalWidgetCategories
 import com.android.systemui.communal.data.repository.CommunalSettingsRepository
+import com.android.systemui.communal.shared.model.CommunalBackgroundType
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.log.dagger.CommunalTableLog
@@ -30,6 +31,7 @@
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor
 import java.util.concurrent.Executor
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
@@ -38,6 +40,7 @@
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
@@ -47,6 +50,7 @@
 @Inject
 constructor(
     @Background private val bgScope: CoroutineScope,
+    @Background private val bgDispatcher: CoroutineDispatcher,
     @Background private val bgExecutor: Executor,
     private val repository: CommunalSettingsRepository,
     userInteractor: SelectedUserInteractor,
@@ -78,6 +82,12 @@
                 initialValue = CommunalWidgetCategories.defaultCategories
             )
 
+    /** The type of background to use for the hub. Used to experiment with different backgrounds */
+    val communalBackground: Flow<CommunalBackgroundType> =
+        userInteractor.selectedUserInfo
+            .flatMapLatest { user -> repository.getBackground(user) }
+            .flowOn(bgDispatcher)
+
     private val workProfileUserInfoCallbackFlow: Flow<UserInfo?> = conflatedCallbackFlow {
         fun send(profiles: List<UserInfo>) {
             trySend(profiles.find { it.isManagedProfile })
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt
similarity index 72%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt
copy to packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt
index 277dbb7..8b816db 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt
@@ -14,8 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.communal.shared.model
 
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.iconLabelVisibilityRepository by Kosmos.Fixture { IconLabelVisibilityRepository() }
+/** Models the types of background that can be shown on the hub. */
+enum class CommunalBackgroundType(val value: Int) {
+    DEFAULT(0),
+    STATIC_GRADIENT(1),
+    ANIMATED(2),
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index db251fd..8cd5603 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -23,6 +23,7 @@
 import com.android.compose.animation.scene.SceneKey
 import com.android.compose.animation.scene.TransitionKey
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
 import com.android.systemui.communal.domain.model.CommunalContentModel
 import com.android.systemui.communal.widgets.WidgetConfigurator
 import com.android.systemui.media.controls.ui.view.MediaHost
@@ -33,10 +34,11 @@
 
 /** The base view model for the communal hub. */
 abstract class BaseCommunalViewModel(
+    private val communalSceneInteractor: CommunalSceneInteractor,
     private val communalInteractor: CommunalInteractor,
     val mediaHost: MediaHost,
 ) {
-    val currentScene: Flow<SceneKey> = communalInteractor.desiredScene
+    val currentScene: Flow<SceneKey> = communalSceneInteractor.currentScene
 
     /** Whether communal hub should be focused by accessibility tools. */
     open val isFocusable: Flow<Boolean> = MutableStateFlow(false)
@@ -58,7 +60,7 @@
     }
 
     fun changeScene(scene: SceneKey, transitionKey: TransitionKey? = null) {
-        communalInteractor.changeScene(scene, transitionKey)
+        communalSceneInteractor.changeScene(scene, transitionKey)
     }
 
     /**
@@ -67,7 +69,7 @@
      * Note that you must call is with `null` when the UI is done or risk a memory leak.
      */
     fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
-        communalInteractor.setTransitionState(transitionState)
+        communalSceneInteractor.setTransitionState(transitionState)
     }
 
     /**
@@ -89,6 +91,12 @@
     /** A list of all the communal content to be displayed in the communal hub. */
     abstract val communalContent: Flow<List<CommunalContentModel>>
 
+    /**
+     * Whether to freeze the emission of the communalContent flow to prevent recomposition. Defaults
+     * to false, indicating that the flow will emit new update.
+     */
+    open val isCommunalContentFlowFrozen: Flow<Boolean> = flowOf(false)
+
     /** Whether in edit mode for the communal hub. */
     open val isEditMode = false
 
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index 650852c..bc65ccb 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -25,6 +25,7 @@
 import androidx.activity.result.ActivityResultLauncher
 import com.android.internal.logging.UiEventLogger
 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.domain.model.CommunalContentModel
 import com.android.systemui.communal.shared.log.CommunalUiEvent
@@ -50,13 +51,14 @@
 class CommunalEditModeViewModel
 @Inject
 constructor(
+    communalSceneInteractor: CommunalSceneInteractor,
     private val communalInteractor: CommunalInteractor,
     private val communalSettingsInteractor: CommunalSettingsInteractor,
     @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
     private val uiEventLogger: UiEventLogger,
     @CommunalLog logBuffer: LogBuffer,
     @Background private val backgroundDispatcher: CoroutineDispatcher,
-) : BaseCommunalViewModel(communalInteractor, mediaHost) {
+) : BaseCommunalViewModel(communalSceneInteractor, communalInteractor, mediaHost) {
 
     private val logger = Logger(logBuffer, "CommunalEditModeViewModel")
 
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 97db43b..3e00b04 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
@@ -20,8 +20,11 @@
 import android.view.View
 import android.view.accessibility.AccessibilityNodeInfo
 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.domain.interactor.CommunalTutorialInteractor
 import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.shared.model.CommunalBackgroundType
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Main
@@ -37,7 +40,9 @@
 import com.android.systemui.media.dagger.MediaModule
 import com.android.systemui.res.R
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
 import com.android.systemui.util.kotlin.BooleanFlowOperators.not
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
 import javax.inject.Inject
 import javax.inject.Named
 import kotlinx.coroutines.CoroutineScope
@@ -65,17 +70,22 @@
     @Main private val resources: Resources,
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
     keyguardInteractor: KeyguardInteractor,
+    communalSceneInteractor: CommunalSceneInteractor,
     private val communalInteractor: CommunalInteractor,
+    private val communalSettingsInteractor: CommunalSettingsInteractor,
     tutorialInteractor: CommunalTutorialInteractor,
     private val shadeInteractor: ShadeInteractor,
     @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
     @CommunalLog logBuffer: LogBuffer,
-) : BaseCommunalViewModel(communalInteractor, mediaHost) {
+) : BaseCommunalViewModel(communalSceneInteractor, communalInteractor, mediaHost) {
 
     private val logger = Logger(logBuffer, "CommunalViewModel")
 
+    /** Communal content saved from the previous emission when the flow is active (not "frozen"). */
+    private var frozenCommunalContent: List<CommunalContentModel>? = null
+
     @OptIn(ExperimentalCoroutinesApi::class)
-    override val communalContent: Flow<List<CommunalContentModel>> =
+    private val latestCommunalContent: Flow<List<CommunalContentModel>> =
         tutorialInteractor.isTutorialAvailable
             .flatMapLatest { isTutorialMode ->
                 if (isTutorialMode) {
@@ -91,9 +101,40 @@
                 }
             }
             .onEach { models ->
+                frozenCommunalContent = models
                 logger.d({ "Content updated: $str1" }) { str1 = models.joinToString { it.key } }
             }
 
+    /**
+     * Freeze the content flow, when an activity is about to show, like starting a timer via voice:
+     * 1) in handheld mode, use the keyguard occluded state;
+     * 2) in dreaming mode, where keyguard is already occluded by dream, use the dream wakeup
+     *    signal. Since in this case the shell transition info does not include
+     *    KEYGUARD_VISIBILITY_TRANSIT_FLAGS, KeyguardTransitionHandler will not run the
+     *    occludeAnimation on KeyguardViewMediator.
+     */
+    override val isCommunalContentFlowFrozen: Flow<Boolean> =
+        allOf(
+                keyguardTransitionInteractor.isFinishedInState(KeyguardState.GLANCEABLE_HUB),
+                keyguardInteractor.isKeyguardOccluded,
+                not(keyguardInteractor.isAbleToDream)
+            )
+            .distinctUntilChanged()
+            .onEach { logger.d("isCommunalContentFlowFrozen: $it") }
+
+    override val communalContent: Flow<List<CommunalContentModel>> =
+        isCommunalContentFlowFrozen
+            .flatMapLatestConflated { isFrozen ->
+                if (isFrozen) {
+                    flowOf(frozenCommunalContent ?: emptyList())
+                } else {
+                    latestCommunalContent
+                }
+            }
+            .onEach { models ->
+                logger.d({ "CommunalContent: $str1" }) { str1 = models.joinToString { it.key } }
+            }
+
     override val isEmptyState: Flow<Boolean> =
         communalInteractor.widgetContent
             .map { it.isEmpty() }
@@ -246,6 +287,10 @@
      */
     val showGestureIndicator: Flow<Boolean> = not(keyguardInteractor.isDreaming)
 
+    /** The type of background to use for the hub. */
+    val communalBackground: Flow<CommunalBackgroundType> =
+        communalSettingsInteractor.communalBackground
+
     companion object {
         const val POPUP_AUTO_HIDE_TIMEOUT_MS = 12000L
     }
@@ -253,5 +298,6 @@
 
 sealed class PopupType {
     object CtaTile : PopupType()
+
     object CustomizeWidgetButton : PopupType()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
index 778d8cf..51a3a6d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
@@ -61,6 +61,7 @@
 
         activityStarter.startPendingIntentMaybeDismissingKeyguard(
             pendingIntent,
+            /* dismissShade = */ false,
             /* intentSentUiThreadCallback = */ null,
             animationController,
             fillInIntent,
diff --git a/packages/SystemUI/src/com/android/systemui/complication/ComplicationTypesUpdater.java b/packages/SystemUI/src/com/android/systemui/complication/ComplicationTypesUpdater.java
index 0bdc7f1..84807fb 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/ComplicationTypesUpdater.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/ComplicationTypesUpdater.java
@@ -69,15 +69,15 @@
             }
         };
 
-        mSecureSettings.registerContentObserverForUser(
+        mSecureSettings.registerContentObserverForUserSync(
                 Settings.Secure.SCREENSAVER_COMPLICATIONS_ENABLED,
                 settingsObserver,
                 UserHandle.myUserId());
-        mSecureSettings.registerContentObserverForUser(
+        mSecureSettings.registerContentObserverForUserSync(
                 Settings.Secure.SCREENSAVER_HOME_CONTROLS_ENABLED,
                 settingsObserver,
                 UserHandle.myUserId());
-        mSecureSettings.registerContentObserverForUser(
+        mSecureSettings.registerContentObserverForUserSync(
                 Settings.Secure.LOCKSCREEN_SHOW_CONTROLS,
                 settingsObserver,
                 UserHandle.myUserId());
diff --git a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
index afa2375..e284bc7 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/DreamHomeControlsComplication.java
@@ -18,6 +18,7 @@
 
 import static com.android.systemui.complication.dagger.DreamHomeControlsComplicationComponent.DreamHomeControlsModule.DREAM_HOME_CONTROLS_CHIP_VIEW;
 import static com.android.systemui.complication.dagger.RegisteredComplicationsModule.DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS;
+import static com.android.systemui.complication.dagger.RegisteredComplicationsModule.OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS;
 import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE;
 import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK;
 import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.UNAVAILABLE;
@@ -35,6 +36,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.settingslib.Utils;
 import com.android.systemui.CoreStartable;
+import com.android.systemui.Flags;
 import com.android.systemui.animation.ActivityTransitionAnimator;
 import com.android.systemui.complication.dagger.DreamHomeControlsComplicationComponent;
 import com.android.systemui.controls.ControlsServiceInfo;
@@ -89,6 +91,7 @@
         private final DreamHomeControlsComplication mComplication;
         private final DreamOverlayStateController mDreamOverlayStateController;
         private final ControlsComponent mControlsComponent;
+        private final boolean mReplacedByOpenHub;
 
         private boolean mOverlayActive = false;
 
@@ -116,11 +119,13 @@
         public Registrant(DreamHomeControlsComplication complication,
                 DreamOverlayStateController dreamOverlayStateController,
                 ControlsComponent controlsComponent,
-                @SystemUser Monitor monitor) {
+                @SystemUser Monitor monitor,
+                @Named(OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS) boolean replacedByOpenHub) {
             super(monitor);
             mComplication = complication;
             mControlsComponent = controlsComponent;
             mDreamOverlayStateController = dreamOverlayStateController;
+            mReplacedByOpenHub = replacedByOpenHub;
         }
 
         @Override
@@ -132,7 +137,9 @@
 
         private void updateHomeControlsComplication() {
             mControlsComponent.getControlsListingController().ifPresent(c -> {
-                if (isHomeControlsAvailable(c.getCurrentServices())) {
+                final boolean replacedWithOpenHub =
+                        Flags.glanceableHubShortcutButton() && mReplacedByOpenHub;
+                if (isHomeControlsAvailable(c.getCurrentServices()) && !replacedWithOpenHub) {
                     mDreamOverlayStateController.addComplication(mComplication);
                 } else {
                     mDreamOverlayStateController.removeComplication(mComplication);
diff --git a/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java b/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java
new file mode 100644
index 0000000..a679bfb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.complication;
+
+import static com.android.systemui.complication.dagger.OpenHubComplicationComponent.OpenHubModule.OPEN_HUB_CHIP_VIEW;
+import static com.android.systemui.complication.dagger.RegisteredComplicationsModule.OPEN_HUB_CHIP_LAYOUT_PARAMS;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.settingslib.Utils;
+import com.android.systemui.CoreStartable;
+import com.android.systemui.Flags;
+import com.android.systemui.communal.domain.interactor.CommunalInteractor;
+import com.android.systemui.communal.shared.model.CommunalScenes;
+import com.android.systemui.complication.dagger.OpenHubComplicationComponent;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.SystemUser;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.shared.condition.Monitor;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.util.ViewController;
+import com.android.systemui.util.condition.ConditionalCoreStartable;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * A dream complication that shows a chip to open the glanceable hub.
+ */
+// TODO(b/339667383): delete or properly implement this once a product decision is made
+public class OpenHubComplication implements Complication {
+    private final Resources mResources;
+    private final OpenHubComplicationComponent.Factory mComponentFactory;
+
+    @Inject
+    public OpenHubComplication(
+            @Main Resources resources,
+            OpenHubComplicationComponent.Factory componentFactory) {
+        mResources = resources;
+        mComponentFactory = componentFactory;
+    }
+
+    @Override
+    public ViewHolder createView(ComplicationViewModel model) {
+        return mComponentFactory.create(mResources).getViewHolder();
+    }
+
+    @Override
+    public int getRequiredTypeAvailability() {
+        // TODO(b/339667383): create a new complication type if we decide to productionize this
+        return COMPLICATION_TYPE_NONE;
+    }
+
+    /**
+     * {@link CoreStartable} for registering the complication with SystemUI on startup.
+     */
+    public static class Registrant extends ConditionalCoreStartable {
+        private final OpenHubComplication mComplication;
+        private final DreamOverlayStateController mDreamOverlayStateController;
+
+        private boolean mOverlayActive = false;
+
+        private final DreamOverlayStateController.Callback mOverlayStateCallback =
+                new DreamOverlayStateController.Callback() {
+                    @Override
+                    public void onStateChanged() {
+                        if (mOverlayActive == mDreamOverlayStateController.isOverlayActive()) {
+                            return;
+                        }
+
+                        mOverlayActive = !mOverlayActive;
+
+                        if (mOverlayActive) {
+                            updateOpenHubComplication();
+                        }
+                    }
+                };
+
+        @Inject
+        public Registrant(OpenHubComplication complication,
+                DreamOverlayStateController dreamOverlayStateController,
+                @SystemUser Monitor monitor) {
+            super(monitor);
+            mComplication = complication;
+            mDreamOverlayStateController = dreamOverlayStateController;
+        }
+
+        @Override
+        public void onStart() {
+            mDreamOverlayStateController.addCallback(mOverlayStateCallback);
+        }
+
+        private void updateOpenHubComplication() {
+            // TODO(b/339667383): don't show the complication if glanceable hub is disabled
+            if (Flags.glanceableHubShortcutButton()) {
+                mDreamOverlayStateController.addComplication(mComplication);
+            } else {
+                mDreamOverlayStateController.removeComplication(mComplication);
+            }
+        }
+    }
+
+    /**
+     * Contains values/logic associated with the dream complication view.
+     */
+    public static class OpenHubChipViewHolder implements ViewHolder {
+        private final ImageView mView;
+        private final ComplicationLayoutParams mLayoutParams;
+        private final OpenHubChipViewController mViewController;
+
+        @Inject
+        OpenHubChipViewHolder(
+                OpenHubChipViewController dreamOpenHubChipViewController,
+                @Named(OPEN_HUB_CHIP_VIEW) ImageView view,
+                @Named(OPEN_HUB_CHIP_LAYOUT_PARAMS) ComplicationLayoutParams layoutParams
+        ) {
+            mView = view;
+            mLayoutParams = layoutParams;
+            mViewController = dreamOpenHubChipViewController;
+            mViewController.init();
+        }
+
+        @Override
+        public ImageView getView() {
+            return mView;
+        }
+
+        @Override
+        public ComplicationLayoutParams getLayoutParams() {
+            return mLayoutParams;
+        }
+    }
+
+    /**
+     * Controls behavior of the dream complication.
+     */
+    static class OpenHubChipViewController extends ViewController<ImageView> {
+        private static final String TAG = "OpenHubCtrl";
+        private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+        private final Context mContext;
+        private final ConfigurationController mConfigurationController;
+
+        private final ConfigurationController.ConfigurationListener mConfigurationListener =
+                new ConfigurationController.ConfigurationListener() {
+                    @Override
+                    public void onUiModeChanged() {
+                        reloadResources();
+                    }
+                };
+        private final CommunalInteractor mCommunalInteractor;
+
+        @Inject
+        OpenHubChipViewController(
+                @Named(OPEN_HUB_CHIP_VIEW) ImageView view,
+                Context context,
+                ConfigurationController configurationController,
+                CommunalInteractor communalInteractor) {
+            super(view);
+
+            mContext = context;
+            mConfigurationController = configurationController;
+            mCommunalInteractor = communalInteractor;
+        }
+
+        @Override
+        protected void onViewAttached() {
+            reloadResources();
+            mView.setOnClickListener(this::onClickOpenHub);
+            mConfigurationController.addCallback(mConfigurationListener);
+        }
+
+        @Override
+        protected void onViewDetached() {
+            mConfigurationController.removeCallback(mConfigurationListener);
+        }
+
+        private void reloadResources() {
+            mView.setImageTintList(Utils.getColorAttr(mContext, android.R.attr.textColorPrimary));
+            final Drawable background = mView.getBackground();
+            if (background != null) {
+                background.setTintList(
+                        Utils.getColorAttr(mContext, com.android.internal.R.attr.colorSurface));
+            }
+        }
+
+        private void onClickOpenHub(View v) {
+            if (DEBUG) Log.d(TAG, "open hub complication tapped");
+
+            mCommunalInteractor.changeScene(CommunalScenes.Communal, null);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/OpenHubComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/complication/dagger/OpenHubComplicationComponent.java
new file mode 100644
index 0000000..501601e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/complication/dagger/OpenHubComplicationComponent.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.complication.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.view.LayoutInflater;
+import android.widget.ImageView;
+
+import com.android.systemui.complication.OpenHubComplication;
+import com.android.systemui.res.R;
+import com.android.systemui.shared.shadow.DoubleShadowIconDrawable;
+import com.android.systemui.shared.shadow.DoubleShadowTextHelper;
+
+import dagger.BindsInstance;
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Named;
+import javax.inject.Scope;
+
+/**
+ * Responsible for generating dependencies for the {@link OpenHubComplication}.
+ */
+@Subcomponent(modules = OpenHubComplicationComponent.OpenHubModule.class)
+@OpenHubComplicationComponent.OpenHubComplicationScope
+public interface OpenHubComplicationComponent {
+    /**
+     * Creates a view holder for the open hub complication.
+     */
+    OpenHubComplication.OpenHubChipViewHolder getViewHolder();
+
+    /**
+     * Scope of the open hub complication.
+     */
+    @Documented
+    @Retention(RUNTIME)
+    @Scope
+    @interface OpenHubComplicationScope {
+    }
+
+    /**
+     * Factory that generates a {@link OpenHubComplicationComponent}.
+     */
+    @Subcomponent.Factory
+    interface Factory {
+        /**
+         * Creates an instance of {@link OpenHubComplicationComponent}.
+         */
+        OpenHubComplicationComponent create(@BindsInstance Resources resources);
+    }
+
+    /**
+     * Scoped injected values for the {@link OpenHubComplicationComponent}.
+     */
+    @Module
+    interface OpenHubModule {
+        String OPEN_HUB_CHIP_VIEW = "open_hub_chip_view";
+        String OPEN_HUB_BACKGROUND_DRAWABLE = "open_hub_background_drawable";
+
+        /**
+         * Provides the dream open hub chip view.
+         */
+        @Provides
+        @OpenHubComplicationScope
+        @Named(OPEN_HUB_CHIP_VIEW)
+        static ImageView provideOpenHubChipView(
+                LayoutInflater layoutInflater,
+                @Named(OPEN_HUB_BACKGROUND_DRAWABLE) Drawable backgroundDrawable) {
+            final ImageView chip =
+                    (ImageView) layoutInflater.inflate(R.layout.dream_overlay_open_hub_chip,
+                            null, false);
+            chip.setBackground(backgroundDrawable);
+
+            return chip;
+        }
+
+        /**
+         * Provides the background drawable for the open hub chip.
+         */
+        @Provides
+        @OpenHubComplicationScope
+        @Named(OPEN_HUB_BACKGROUND_DRAWABLE)
+        static Drawable providesOpenHubBackground(Context context, Resources resources) {
+            return new DoubleShadowIconDrawable(createShadowInfo(
+                    resources,
+                    R.dimen.dream_overlay_bottom_affordance_key_text_shadow_radius,
+                    R.dimen.dream_overlay_bottom_affordance_key_text_shadow_dx,
+                    R.dimen.dream_overlay_bottom_affordance_key_text_shadow_dy,
+                    R.dimen.dream_overlay_bottom_affordance_key_shadow_alpha
+            ),
+                    createShadowInfo(
+                            resources,
+                            R.dimen.dream_overlay_bottom_affordance_ambient_text_shadow_radius,
+                            R.dimen.dream_overlay_bottom_affordance_ambient_text_shadow_dx,
+                            R.dimen.dream_overlay_bottom_affordance_ambient_text_shadow_dy,
+                            R.dimen.dream_overlay_bottom_affordance_ambient_shadow_alpha
+                    ),
+                    resources.getDrawable(R.drawable.dream_overlay_bottom_affordance_bg),
+                    resources.getDimensionPixelOffset(
+                            R.dimen.dream_overlay_bottom_affordance_width),
+                    resources.getDimensionPixelSize(R.dimen.dream_overlay_bottom_affordance_inset)
+            );
+        }
+
+        private static DoubleShadowTextHelper.ShadowInfo createShadowInfo(Resources resources,
+                int blurId, int offsetXId, int offsetYId, int alphaId) {
+
+            return new DoubleShadowTextHelper.ShadowInfo(
+                    resources.getDimension(blurId),
+                    resources.getDimension(offsetXId),
+                    resources.getDimension(offsetYId),
+                    resources.getFloat(alphaId)
+            );
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
index 6f1b098..edb5ff7 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/dagger/RegisteredComplicationsModule.java
@@ -25,6 +25,7 @@
 import com.android.systemui.flags.FeatureFlags;
 import com.android.systemui.flags.Flags;
 import com.android.systemui.res.R;
+import com.android.systemui.util.settings.SystemSettings;
 
 import dagger.Module;
 import dagger.Provides;
@@ -39,6 +40,7 @@
         subcomponents = {
                 DreamClockTimeComplicationComponent.class,
                 DreamHomeControlsComplicationComponent.class,
+                OpenHubComplicationComponent.class,
                 DreamMediaEntryComplicationComponent.class
         })
 public interface RegisteredComplicationsModule {
@@ -46,6 +48,8 @@
     String DREAM_SMARTSPACE_LAYOUT_PARAMS = "smartspace_layout_params";
     String DREAM_HOME_CONTROLS_CHIP_LAYOUT_PARAMS = "home_controls_chip_layout_params";
     String DREAM_MEDIA_ENTRY_LAYOUT_PARAMS = "media_entry_layout_params";
+    String OPEN_HUB_CHIP_LAYOUT_PARAMS = "open_hub_chip_layout_params";
+    String OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS = "open_hub_chip_replace_home_controls";
 
     int DREAM_CLOCK_TIME_COMPLICATION_WEIGHT = 1;
     int DREAM_CLOCK_TIME_COMPLICATION_WEIGHT_NO_SMARTSPACE = 2;
@@ -109,6 +113,26 @@
     }
 
     /**
+     * Provides layout parameters for the open hub complication.
+     */
+    @Provides
+    @Named(OPEN_HUB_CHIP_LAYOUT_PARAMS)
+    static ComplicationLayoutParams provideOpenHubLayoutParams(
+            @Named(OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS) boolean replaceHomeControls) {
+        int position = ComplicationLayoutParams.POSITION_BOTTOM | (replaceHomeControls
+                ? ComplicationLayoutParams.POSITION_START
+                : ComplicationLayoutParams.POSITION_END);
+        int direction = replaceHomeControls ? ComplicationLayoutParams.DIRECTION_END
+                : ComplicationLayoutParams.DIRECTION_START;
+        return new ComplicationLayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                position,
+                direction,
+                DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT);
+    }
+
+    /**
      * Provides layout parameters for the smartspace complication.
      */
     @Provides
@@ -124,4 +148,14 @@
                 res.getDimensionPixelSize(R.dimen.dream_overlay_complication_smartspace_padding),
                 res.getDimensionPixelSize(R.dimen.dream_overlay_complication_smartspace_max_width));
     }
+
+    /**
+     * If true, the home controls chip should not be shown and the open hub chip should be shown in
+     * its place.
+     */
+    @Provides
+    @Named(OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS)
+    static boolean providesOpenHubChipReplaceHomeControls(SystemSettings systemSettings) {
+        return systemSettings.getBool(OPEN_HUB_CHIP_REPLACE_HOME_CONTROLS, false);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index 3e98fc1..7aab37e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -32,6 +32,8 @@
 import com.android.systemui.dock.DockManagerImpl;
 import com.android.systemui.doze.DozeHost;
 import com.android.systemui.keyguard.ui.composable.blueprint.DefaultBlueprintModule;
+import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule;
+import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule;
 import com.android.systemui.media.dagger.MediaModule;
 import com.android.systemui.media.muteawait.MediaMuteAwaitConnectionCli;
 import com.android.systemui.media.nearby.NearbyMediaDevicesManager;
@@ -112,6 +114,8 @@
         GestureModule.class,
         HeadsUpModule.class,
         KeyboardShortcutsModule.class,
+        KeyguardBlueprintModule.class,
+        KeyguardSectionsModule.class,
         MediaModule.class,
         MediaMuteAwaitConnectionCli.StartableModule.class,
         MultiUserUtilsModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 93f3793..2ebb94f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -70,12 +70,11 @@
 import com.android.systemui.keyboard.KeyboardModule;
 import com.android.systemui.keyevent.data.repository.KeyEventRepositoryModule;
 import com.android.systemui.keyguard.ui.composable.LockscreenContent;
-import com.android.systemui.keyguard.ui.view.layout.blueprints.KeyguardBlueprintModule;
-import com.android.systemui.keyguard.ui.view.layout.sections.KeyguardSectionsModule;
 import com.android.systemui.log.dagger.LogModule;
 import com.android.systemui.log.dagger.MonitorLog;
 import com.android.systemui.log.table.TableLogBuffer;
-import com.android.systemui.mediaprojection.appselector.MediaProjectionModule;
+import com.android.systemui.mediaprojection.MediaProjectionModule;
+import com.android.systemui.mediaprojection.appselector.MediaProjectionActivitiesModule;
 import com.android.systemui.mediaprojection.taskswitcher.MediaProjectionTaskSwitcherModule;
 import com.android.systemui.model.SceneContainerPlugin;
 import com.android.systemui.model.SysUiState;
@@ -221,10 +220,9 @@
         InputMethodModule.class,
         KeyEventRepositoryModule.class,
         KeyboardModule.class,
-        KeyguardBlueprintModule.class,
-        KeyguardSectionsModule.class,
         LetterboxModule.class,
         LogModule.class,
+        MediaProjectionActivitiesModule.class,
         MediaProjectionModule.class,
         MediaProjectionTaskSwitcherModule.class,
         MotionToolModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 3194942..7ae8409 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -426,7 +426,7 @@
         }
 
         if (!anyListening) {
-            mSecureSettings.unregisterContentObserver(mSettingsObserver);
+            mSecureSettings.unregisterContentObserverSync(mSettingsObserver);
         } else if (!mSettingRegistered) {
             for (TriggerSensor s : mTriggerSensors) {
                 s.registerSettingsObserver(mSettingsObserver);
@@ -750,7 +750,7 @@
 
         public void registerSettingsObserver(ContentObserver settingsObserver) {
             if (mConfigured && !TextUtils.isEmpty(mSetting)) {
-                mSecureSettings.registerContentObserverForUser(
+                mSecureSettings.registerContentObserverForUserSync(
                         mSetting, mSettingsObserver, UserHandle.USER_ALL);
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index aa7a7da..c6c57479 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -18,6 +18,7 @@
 
 import static android.service.dreams.Flags.dreamWakeRedirect;
 
+import static com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming;
 import static com.android.systemui.dreams.dagger.DreamModule.DREAM_OVERLAY_WINDOW_TITLE;
 import static com.android.systemui.dreams.dagger.DreamModule.DREAM_TOUCH_INSET_MANAGER;
 import static com.android.systemui.dreams.dagger.DreamModule.HOME_CONTROL_PANEL_DREAM_COMPONENT;
@@ -395,7 +396,7 @@
             return;
         }
 
-        redirectWake(mCommunalAvailable);
+        redirectWake(mCommunalAvailable && !glanceableHubAllowKeyguardWhenDreaming());
     }
 
     @Override
@@ -543,7 +544,11 @@
         mStateController.setEntryAnimationsFinished(false);
 
         mDreamOverlayContainerViewController = null;
-        mTouchMonitor = null;
+
+        if (mTouchMonitor != null) {
+            mTouchMonitor.destroy();
+            mTouchMonitor = null;
+        }
 
         mWindow = null;
         mStarted = false;
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 b0d134f..f6ac7a5 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -33,8 +33,8 @@
 import com.android.systemui.dreams.DreamOverlayService;
 import com.android.systemui.dreams.SystemDialogsCloser;
 import com.android.systemui.dreams.complication.dagger.ComplicationComponent;
-import com.android.systemui.dreams.homecontrols.DreamActivityProvider;
-import com.android.systemui.dreams.homecontrols.DreamActivityProviderImpl;
+import com.android.systemui.dreams.homecontrols.DreamServiceDelegate;
+import com.android.systemui.dreams.homecontrols.DreamServiceDelegateImpl;
 import com.android.systemui.dreams.homecontrols.HomeControlsDreamService;
 import com.android.systemui.qs.QsEventLogger;
 import com.android.systemui.qs.pipeline.shared.TileSpec;
@@ -202,8 +202,8 @@
     }
 
 
-    /** Provides activity for dream service */
+    /** Provides delegate to allow for testing of dream service */
     @Binds
-    DreamActivityProvider bindActivityProvider(DreamActivityProviderImpl impl);
+    DreamServiceDelegate bindDreamDelegate(DreamServiceDelegateImpl impl);
 
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamServiceDelegate.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamServiceDelegate.kt
new file mode 100644
index 0000000..2cfb02e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamServiceDelegate.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.dreams.homecontrols
+
+import android.app.Activity
+import android.service.dreams.DreamService
+
+/** Provides abstraction for [DreamService] methods, so they can be mocked in tests. */
+interface DreamServiceDelegate {
+    /** Wrapper for [DreamService.getActivity] which can be mocked in tests. */
+    fun getActivity(dreamService: DreamService): Activity?
+
+    /** Wrapper for [DreamService.wakeUp] which can be mocked in tests. */
+    fun wakeUp(dreamService: DreamService)
+
+    /** Wrapper for [DreamService.finish] which can be mocked in tests. */
+    fun finish(dreamService: DreamService)
+
+    /** Wrapper for [DreamService.getRedirectWake] which can be mocked in tests. */
+    fun redirectWake(dreamService: DreamService): Boolean
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamActivityProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamServiceDelegateImpl.kt
similarity index 69%
rename from packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamActivityProviderImpl.kt
rename to packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamServiceDelegateImpl.kt
index 0854e93..7dc5434 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamActivityProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamServiceDelegateImpl.kt
@@ -19,8 +19,20 @@
 import android.service.dreams.DreamService
 import javax.inject.Inject
 
-class DreamActivityProviderImpl @Inject constructor() : DreamActivityProvider {
+class DreamServiceDelegateImpl @Inject constructor() : DreamServiceDelegate {
     override fun getActivity(dreamService: DreamService): Activity {
         return dreamService.activity
     }
+
+    override fun finish(dreamService: DreamService) {
+        dreamService.finish()
+    }
+
+    override fun wakeUp(dreamService: DreamService) {
+        dreamService.wakeUp()
+    }
+
+    override fun redirectWake(dreamService: DreamService): Boolean {
+        return dreamService.redirectWake
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt
index 76187c6..77c54ec 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/HomeControlsDreamService.kt
@@ -31,6 +31,7 @@
 import com.android.systemui.util.wakelock.WakeLock
 import com.android.systemui.util.wakelock.WakeLock.Builder.NO_TIMEOUT
 import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
 import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
@@ -46,7 +47,7 @@
     private val taskFragmentFactory: TaskFragmentComponent.Factory,
     private val homeControlsComponentInteractor: HomeControlsComponentInteractor,
     private val wakeLockBuilder: WakeLock.Builder,
-    private val dreamActivityProvider: DreamActivityProvider,
+    private val dreamServiceDelegate: DreamServiceDelegate,
     @Background private val bgDispatcher: CoroutineDispatcher,
     @DreamLog logBuffer: LogBuffer
 ) : DreamService() {
@@ -65,7 +66,7 @@
 
     override fun onAttachedToWindow() {
         super.onAttachedToWindow()
-        val activity = dreamActivityProvider.getActivity(this)
+        val activity = dreamServiceDelegate.getActivity(this)
         if (activity == null) {
             finish()
             return
@@ -79,9 +80,9 @@
             taskFragmentFactory
                 .create(
                     activity = activity,
-                    onCreateCallback = this::onTaskFragmentCreated,
+                    onCreateCallback = { launchActivity() },
                     onInfoChangedCallback = this::onTaskFragmentInfoChanged,
-                    hide = { endDream() }
+                    hide = { endDream(false) }
                 )
                 .apply { createTaskFragment() }
 
@@ -91,16 +92,24 @@
     private fun onTaskFragmentInfoChanged(taskFragmentInfo: TaskFragmentInfo) {
         if (taskFragmentInfo.isEmpty) {
             logger.d("Finishing dream due to TaskFragment being empty")
-            endDream()
+            endDream(true)
         }
     }
 
-    private fun endDream() {
+    private fun endDream(handleRedirect: Boolean) {
         homeControlsComponentInteractor.onDreamEndUnexpectedly()
-        finish()
+        if (handleRedirect && dreamServiceDelegate.redirectWake(this)) {
+            dreamServiceDelegate.wakeUp(this)
+            serviceScope.launch {
+                delay(ACTIVITY_RESTART_DELAY)
+                launchActivity()
+            }
+        } else {
+            dreamServiceDelegate.finish(this)
+        }
     }
 
-    private fun onTaskFragmentCreated(taskFragmentInfo: TaskFragmentInfo) {
+    private fun launchActivity() {
         val setting = controlsSettingsRepository.allowActionOnTrivialControlsInLockscreen.value
         val componentName = homeControlsComponentInteractor.panelComponent.value
         logger.d("Starting embedding $componentName")
@@ -134,6 +143,14 @@
          * complete.
          */
         val CANCELLATION_DELAY_AFTER_DETACHED = 5.seconds
+
+        /**
+         * Defines the delay after wakeup where we should attempt to restart the embedded activity.
+         * When a wakeup is redirected, the dream service may keep running. In this case, we should
+         * restart the activity if it finished. This delays ensures the activity is only restarted
+         * after the wakeup transition has played.
+         */
+        val ACTIVITY_RESTART_DELAY = 334.milliseconds
         const val TAG = "HomeControlsDreamService"
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
index 1c047dd..04fda33 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/CommunalTouchHandler.java
@@ -98,7 +98,7 @@
         // Notification shade window has its own logic to be visible if the hub is open, no need to
         // do anything here other than send touch events over.
         session.registerInputListener(ev -> {
-            surfaces.handleDreamTouch((MotionEvent) ev);
+            surfaces.handleCommunalHubTouch((MotionEvent) ev);
             if (ev != null && ((MotionEvent) ev).getAction() == MotionEvent.ACTION_UP) {
                 var unused = session.pop();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt b/packages/SystemUI/src/com/android/systemui/dreams/ui/viewmodel/DreamViewModel.kt
index c5b3c53..4b07f78 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
@@ -17,6 +17,7 @@
 package com.android.systemui.dreams.ui.viewmodel
 
 import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.Flags.glanceableHubAllowKeyguardWhenDreaming
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.communal.domain.interactor.CommunalInteractor
 import com.android.systemui.communal.shared.model.CommunalScenes
@@ -60,7 +61,7 @@
         val showGlanceableHub =
             communalInteractor.isCommunalEnabled.value &&
                 !keyguardUpdateMonitor.isEncryptedOrLockdown(userTracker.userId)
-        if (showGlanceableHub) {
+        if (showGlanceableHub && !glanceableHubAllowKeyguardWhenDreaming()) {
             communalInteractor.changeScene(CommunalScenes.Communal)
         } else {
             toLockscreenTransitionViewModel.startTransition()
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 2e49919..3d3584e 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -42,14 +42,6 @@
     @JvmField val NULL_FLAG = unreleasedFlag("null_flag")
 
     // 100 - notification
-    // TODO(b/297792660): Tracking Bug
-    @JvmField val UNCLEARED_TRANSIENT_HUN_FIX =
-        releasedFlag("uncleared_transient_hun_fix")
-
-    // TODO(b/298308067): Tracking Bug
-    @JvmField val SWIPE_UNCLEARED_TRANSIENT_VIEW_FIX =
-        releasedFlag("swipe_uncleared_transient_view_fix")
-
     // TODO(b/254512751): Tracking Bug
     val NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
         unreleasedFlag("notification_pipeline_developer_logging")
@@ -68,14 +60,6 @@
             "notification_drag_to_contents"
         )
 
-    /**
-     * This flag controls whether we register a listener for StatsD notification memory reports.
-     * For statsd to actually call the listener however, a server-side toggle needs to be
-     * enabled as well.
-     */
-    val NOTIFICATION_MEMORY_LOGGING_ENABLED =
-            releasedFlag("notification_memory_logging_enabled")
-
     // TODO(b/280783617): Tracking Bug
     @Keep
     @JvmField
@@ -170,12 +154,6 @@
     val WALLPAPER_PICKER_GRID_APPLY_BUTTON =
             unreleasedFlag("wallpaper_picker_grid_apply_button")
 
-    /** Keyguard Migration */
-
-    // TODO(b/297037052): Tracking bug.
-    @JvmField
-    val REMOVE_NPVC_BOTTOM_AREA_USAGE = unreleasedFlag("remove_npvc_bottom_area_usage")
-
     /** Flag meant to guard the talkback fix for the KeyguardIndicationTextView */
     // TODO(b/286563884): Tracking bug
     @JvmField val KEYGUARD_TALKBACK_FIX = unreleasedFlag("keyguard_talkback_fix")
@@ -400,9 +378,6 @@
     val WARN_ON_BLOCKING_BINDER_TRANSACTIONS =
         unreleasedFlag("warn_on_blocking_binder_transactions")
 
-    // TODO:(b/283203305): Tracking bug
-    @JvmField val TRIM_FONT_CACHES_AT_UNLOCK = unreleasedFlag("trim_font_caches_on_unlock")
-
     // TODO(b/298380520): Tracking Bug
     @JvmField
     val USER_TRACKER_BACKGROUND_CALLBACKS = unreleasedFlag("user_tracker_background_callbacks")
@@ -473,14 +448,6 @@
     @JvmField
     val ENABLE_CLOCK_KEYGUARD_PRESENTATION = releasedFlag("enable_clock_keyguard_presentation")
 
-    /** Enable the Compose implementation of the PeopleSpaceActivity. */
-    @JvmField
-    val COMPOSE_PEOPLE_SPACE = releasedFlag("compose_people_space")
-
-    /** Enable the Compose implementation of the Quick Settings footer actions. */
-    @JvmField
-    val COMPOSE_QS_FOOTER_ACTIONS = releasedFlag("compose_qs_footer_actions")
-
     /** Enable the share wifi button in Quick Settings internet dialog. */
     @JvmField
     val SHARE_WIFI_QS_BUTTON = releasedFlag("share_wifi_qs_button")
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 49be03c..1e4fb4f 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -428,7 +428,7 @@
 
         // get notified of phone state changes
         mTelephonyListenerManager.addServiceStateListener(mPhoneStateListener);
-        mGlobalSettings.registerContentObserver(
+        mGlobalSettings.registerContentObserverSync(
                 Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
                 mAirplaneModeObserver);
         mHasVibrator = vibrator.hasVibrator();
@@ -453,7 +453,7 @@
     public void destroy() {
         mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
         mTelephonyListenerManager.removeServiceStateListener(mPhoneStateListener);
-        mGlobalSettings.unregisterContentObserver(mAirplaneModeObserver);
+        mGlobalSettings.unregisterContentObserverSync(mAirplaneModeObserver);
         mConfigurationController.removeCallback(this);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
index ea8d7d7..30b9583 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffect.kt
@@ -19,7 +19,9 @@
 import android.os.VibrationEffect
 import android.view.View
 import androidx.annotation.VisibleForTesting
+import com.android.systemui.animation.Expandable
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.plugins.qs.QSTile
 import com.android.systemui.statusbar.VibratorHelper
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
@@ -51,6 +53,10 @@
     var state = State.IDLE
         private set
 
+    /** The QSTile and Expandable used to perform a long-click and click actions */
+    var qsTile: QSTile? = null
+    var expandable: Expandable? = null
+
     /** Flow for view control and action */
     private val _postedActionType = MutableStateFlow<ActionType?>(null)
     val actionType: Flow<ActionType?> =
@@ -105,6 +111,7 @@
         when (state) {
             State.IDLE -> {
                 setState(State.TIMEOUT_WAIT)
+                _postedActionType.value = ActionType.WAIT_TAP_TIMEOUT
             }
             State.RUNNING_BACKWARDS -> _postedActionType.value = ActionType.CANCEL_ANIMATOR
             else -> {}
@@ -112,16 +119,9 @@
     }
 
     fun handleActionUp() {
-        when (state) {
-            State.TIMEOUT_WAIT -> {
-                _postedActionType.value = ActionType.CLICK
-                setState(State.IDLE)
-            }
-            State.RUNNING_FORWARD -> {
-                _postedActionType.value = ActionType.REVERSE_ANIMATOR
-                setState(State.RUNNING_BACKWARDS)
-            }
-            else -> {}
+        if (state == State.RUNNING_FORWARD) {
+            _postedActionType.value = ActionType.REVERSE_ANIMATOR
+            setState(State.RUNNING_BACKWARDS)
         }
     }
 
@@ -129,6 +129,7 @@
         when (state) {
             State.TIMEOUT_WAIT -> {
                 setState(State.IDLE)
+                clearActionType()
             }
             State.RUNNING_FORWARD -> {
                 _postedActionType.value = ActionType.REVERSE_ANIMATOR
@@ -145,18 +146,23 @@
 
     /** This function is called both when an animator completes or gets cancelled */
     fun handleAnimationComplete() {
-        if (state == State.RUNNING_FORWARD) {
-            vibrate(snapEffect)
-            _postedActionType.value = ActionType.LONG_PRESS
-        }
-        if (state != State.TIMEOUT_WAIT) {
-            // This will happen if the animator did not finish by being cancelled
-            setState(State.IDLE)
+        when (state) {
+            State.RUNNING_FORWARD -> {
+                setState(State.IDLE)
+                vibrate(snapEffect)
+                _postedActionType.value = ActionType.LONG_PRESS
+            }
+            State.RUNNING_BACKWARDS -> {
+                setState(State.IDLE)
+                clearActionType()
+            }
+            else -> {}
         }
     }
 
     fun handleAnimationCancel() {
         setState(State.TIMEOUT_WAIT)
+        _postedActionType.value = ActionType.WAIT_TAP_TIMEOUT
     }
 
     fun handleTimeoutComplete() {
@@ -190,9 +196,22 @@
                 effectDuration
             )
         setState(State.IDLE)
+        clearActionType()
         return true
     }
 
+    fun onTileClick(): Boolean {
+        if (state == State.TIMEOUT_WAIT) {
+            setState(State.IDLE)
+            clearActionType()
+            qsTile?.let {
+                it.click(expandable)
+                return true
+            }
+        }
+        return false
+    }
+
     enum class State {
         IDLE, /* The effect is idle waiting for touch input */
         TIMEOUT_WAIT, /* The effect is waiting for a [PRESSED_TIMEOUT] period */
@@ -202,7 +221,7 @@
 
     /* A type of action to perform on the view depending on the effect's state and logic */
     enum class ActionType {
-        CLICK,
+        WAIT_TAP_TIMEOUT,
         LONG_PRESS,
         RESET_AND_LONG_PRESS,
         START_ANIMATOR,
diff --git a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt
index 4875f48..92a55ef 100644
--- a/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/haptics/qs/QSLongPressEffectViewBinder.kt
@@ -17,8 +17,6 @@
 package com.android.systemui.haptics.qs
 
 import android.animation.ValueAnimator
-import android.annotation.SuppressLint
-import android.view.MotionEvent
 import android.view.ViewConfiguration
 import android.view.animation.AccelerateDecelerateInterpolator
 import androidx.core.animation.doOnCancel
@@ -30,6 +28,7 @@
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.qs.tileimpl.QSTileViewImpl
 import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.delay
 import kotlinx.coroutines.flow.filterNotNull
 
 object QSLongPressEffectViewBinder {
@@ -41,9 +40,6 @@
     ): DisposableHandle? {
         if (qsLongPressEffect == null) return null
 
-        // Set the touch listener as the long-press effect
-        setTouchListener(tile, qsLongPressEffect)
-
         return tile.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.CREATED) {
                 // Action to perform
@@ -52,18 +48,18 @@
 
                     qsLongPressEffect.actionType.filterNotNull().collect { action ->
                         when (action) {
-                            QSLongPressEffect.ActionType.CLICK -> {
-                                tile.performClick()
-                                qsLongPressEffect.clearActionType()
+                            QSLongPressEffect.ActionType.WAIT_TAP_TIMEOUT -> {
+                                delay(ViewConfiguration.getTapTimeout().toLong())
+                                qsLongPressEffect.handleTimeoutComplete()
                             }
                             QSLongPressEffect.ActionType.LONG_PRESS -> {
                                 tile.prepareForLaunch()
-                                tile.performLongClick()
+                                qsLongPressEffect.qsTile?.longClick(qsLongPressEffect.expandable)
                                 qsLongPressEffect.clearActionType()
                             }
                             QSLongPressEffect.ActionType.RESET_AND_LONG_PRESS -> {
                                 tile.resetLongPressEffectProperties()
-                                tile.performLongClick()
+                                qsLongPressEffect.qsTile?.longClick(qsLongPressEffect.expandable)
                                 qsLongPressEffect.clearActionType()
                             }
                             QSLongPressEffect.ActionType.START_ANIMATOR -> {
@@ -106,22 +102,4 @@
             }
         }
     }
-
-    @SuppressLint("ClickableViewAccessibility")
-    private fun setTouchListener(tile: QSTileViewImpl, longPressEffect: QSLongPressEffect?) {
-        tile.setOnTouchListener { _, event ->
-            when (event.actionMasked) {
-                MotionEvent.ACTION_DOWN -> {
-                    tile.postDelayed(
-                        { longPressEffect?.handleTimeoutComplete() },
-                        ViewConfiguration.getTapTimeout().toLong(),
-                    )
-                    longPressEffect?.handleActionDown()
-                }
-                MotionEvent.ACTION_UP -> longPressEffect?.handleActionUp()
-                MotionEvent.ACTION_CANCEL -> longPressEffect?.handleActionCancel()
-            }
-            true
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt
index f16a3bd..90867edd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/PhysicalKeyboardCoreStartable.kt
@@ -19,10 +19,12 @@
 
 import android.hardware.input.InputSettings
 import com.android.systemui.CoreStartable
+import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
+import com.android.systemui.flags.Flags as LegacyFlag
 import com.android.systemui.keyboard.backlight.ui.KeyboardBacklightDialogCoordinator
+import com.android.systemui.keyboard.docking.binder.KeyboardDockingIndicationViewBinder
 import com.android.systemui.keyboard.stickykeys.ui.StickyKeysIndicatorCoordinator
 import dagger.Lazy
 import javax.inject.Inject
@@ -34,14 +36,18 @@
 constructor(
     private val keyboardBacklightDialogCoordinator: Lazy<KeyboardBacklightDialogCoordinator>,
     private val stickyKeysIndicatorCoordinator: Lazy<StickyKeysIndicatorCoordinator>,
+    private val keyboardDockingIndicationViewBinder: Lazy<KeyboardDockingIndicationViewBinder>,
     private val featureFlags: FeatureFlags,
 ) : CoreStartable {
     override fun start() {
-        if (featureFlags.isEnabled(Flags.KEYBOARD_BACKLIGHT_INDICATOR)) {
+        if (featureFlags.isEnabled(LegacyFlag.KEYBOARD_BACKLIGHT_INDICATOR)) {
             keyboardBacklightDialogCoordinator.get().startListening()
         }
         if (InputSettings.isAccessibilityStickyKeysFeatureEnabled()) {
             stickyKeysIndicatorCoordinator.get().startListening()
         }
+        if (Flags.keyboardDockingIndicator()) {
+            keyboardDockingIndicationViewBinder.get().startListening()
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt
new file mode 100644
index 0000000..f649be2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/docking/binder/KeyboardDockingIndicationViewBinder.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.docking.binder
+
+import android.content.Context
+import android.graphics.Paint
+import android.graphics.PixelFormat
+import android.view.WindowManager
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyboard.docking.ui.KeyboardDockingIndicationView
+import com.android.systemui.keyboard.docking.ui.viewmodel.KeyboardDockingIndicationViewModel
+import com.android.systemui.surfaceeffects.PaintDrawCallback
+import com.android.systemui.surfaceeffects.glowboxeffect.GlowBoxEffect
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class KeyboardDockingIndicationViewBinder
+@Inject
+constructor(
+    context: Context,
+    @Application private val applicationScope: CoroutineScope,
+    private val viewModel: KeyboardDockingIndicationViewModel,
+    private val windowManager: WindowManager
+) {
+
+    private val windowLayoutParams =
+        WindowManager.LayoutParams().apply {
+            width = WindowManager.LayoutParams.MATCH_PARENT
+            height = WindowManager.LayoutParams.MATCH_PARENT
+            format = PixelFormat.TRANSLUCENT
+            type = WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
+            fitInsetsTypes = 0 // Ignore insets from all system bars
+            title = "Edge glow effect"
+            flags =
+                (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
+                    WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
+            setTrustedOverlay()
+        }
+
+    private var glowEffect: GlowBoxEffect? = null
+    private val glowEffectView = KeyboardDockingIndicationView(context, null)
+
+    private val drawCallback =
+        object : PaintDrawCallback {
+            override fun onDraw(paint: Paint) {
+                glowEffectView.draw(paint)
+            }
+        }
+
+    private val stateChangedCallback =
+        object : GlowBoxEffect.AnimationStateChangedCallback {
+            override fun onStart() {
+                windowManager.addView(glowEffectView, windowLayoutParams)
+            }
+
+            override fun onEnd() {
+                windowManager.removeView(glowEffectView)
+            }
+        }
+
+    fun startListening() {
+        applicationScope.launch {
+            viewModel.edgeGlow.collect { config ->
+                if (glowEffect == null) {
+                    glowEffect = GlowBoxEffect(config, drawCallback, stateChangedCallback)
+                } else {
+                    glowEffect?.finish(force = true)
+                    glowEffect!!.updateConfig(config)
+                }
+            }
+        }
+
+        applicationScope.launch {
+            viewModel.keyboardConnected.collect { connected ->
+                if (connected) {
+                    glowEffect?.play()
+                } else {
+                    glowEffect?.finish()
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamActivityProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/keyboard/docking/domain/interactor/KeyboardDockingIndicationInteractor.kt
similarity index 60%
copy from packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamActivityProviderImpl.kt
copy to packages/SystemUI/src/com/android/systemui/keyboard/docking/domain/interactor/KeyboardDockingIndicationInteractor.kt
index 0854e93..c670b5e 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamActivityProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/docking/domain/interactor/KeyboardDockingIndicationInteractor.kt
@@ -13,14 +13,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.dreams.homecontrols
 
-import android.app.Activity
-import android.service.dreams.DreamService
+package com.android.systemui.keyboard.docking.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyboard.data.repository.KeyboardRepository
 import javax.inject.Inject
 
-class DreamActivityProviderImpl @Inject constructor() : DreamActivityProvider {
-    override fun getActivity(dreamService: DreamService): Activity {
-        return dreamService.activity
-    }
+/** Listens for keyboard docking event. */
+@SysUISingleton
+class KeyboardDockingIndicationInteractor
+@Inject
+constructor(keyboardRepository: KeyboardRepository) {
+    val onKeyboardConnected = keyboardRepository.isAnyKeyboardConnected
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/KeyboardDockingIndicationView.kt b/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/KeyboardDockingIndicationView.kt
new file mode 100644
index 0000000..de8b2cf
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/KeyboardDockingIndicationView.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.docking.ui
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Paint
+import android.util.AttributeSet
+import android.view.View
+
+/** View that's used for rendering keyboard docking indicator. */
+class KeyboardDockingIndicationView(context: Context?, attrs: AttributeSet?) :
+    View(context, attrs) {
+
+    private var paint: Paint? = null
+
+    override fun onDraw(canvas: Canvas) {
+        if (!canvas.isHardwareAccelerated) {
+            return
+        }
+        paint?.let { canvas.drawPaint(it) }
+    }
+
+    fun draw(paint: Paint) {
+        this.paint = paint
+        invalidate()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt
new file mode 100644
index 0000000..2578b78
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModel.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.docking.ui.viewmodel
+
+import android.content.Context
+import android.view.Surface
+import android.view.WindowManager
+import com.android.settingslib.Utils
+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.keyboard.docking.domain.interactor.KeyboardDockingIndicationInteractor
+import com.android.systemui.surfaceeffects.glowboxeffect.GlowBoxConfig
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class KeyboardDockingIndicationViewModel
+@Inject
+constructor(
+    private val windowManager: WindowManager,
+    private val context: Context,
+    keyboardDockingIndicationInteractor: KeyboardDockingIndicationInteractor,
+    configurationInteractor: ConfigurationInteractor,
+    @Background private val backgroundScope: CoroutineScope,
+) {
+
+    private val _edgeGlow: MutableStateFlow<GlowBoxConfig> = MutableStateFlow(createEffectConfig())
+    val edgeGlow = _edgeGlow.asStateFlow()
+    val keyboardConnected = keyboardDockingIndicationInteractor.onKeyboardConnected
+
+    init {
+        /**
+         * Expected behaviors:
+         * 1) On keyboard docking event, we play the animation for a fixed duration.
+         * 2) If the keyboard gets disconnected during the animation, we finish the animation with
+         *    ease out.
+         * 3) If the configuration changes (e.g., device rotation), we force cancel the animation
+         *    with no ease out.
+         */
+        backgroundScope.launch {
+            configurationInteractor.onAnyConfigurationChange.collect {
+                _edgeGlow.value = createEffectConfig()
+            }
+        }
+    }
+
+    private fun createEffectConfig(): GlowBoxConfig {
+        val bounds = windowManager.currentWindowMetrics.bounds
+        val width = bounds.width().toFloat()
+        val height = bounds.height().toFloat()
+
+        val startCenterX: Float
+        val startCenterY: Float
+        val endCenterX: Float
+        val endCenterY: Float
+        val boxWidth: Float
+        val boxHeight: Float
+
+        when (context.display.rotation) {
+            Surface.ROTATION_0 -> {
+                endCenterX = width
+                endCenterY = height * 0.5f
+                startCenterX = endCenterX + OFFSET
+                startCenterY = endCenterY
+                boxWidth = THICKNESS
+                boxHeight = height
+            }
+            Surface.ROTATION_90 -> {
+                endCenterX = width * 0.5f
+                endCenterY = 0f
+                startCenterX = endCenterX
+                startCenterY = endCenterY - OFFSET
+                boxWidth = width
+                boxHeight = THICKNESS
+            }
+            Surface.ROTATION_180 -> {
+                endCenterX = 0f
+                endCenterY = height * 0.5f
+                startCenterX = endCenterX - OFFSET
+                startCenterY = endCenterY
+                boxWidth = THICKNESS
+                boxHeight = height
+            }
+            Surface.ROTATION_270 -> {
+                endCenterX = width * 0.5f
+                endCenterY = height
+                startCenterX = endCenterX
+                startCenterY = endCenterY + OFFSET
+                boxWidth = width
+                boxHeight = THICKNESS
+            }
+            else -> { // Shouldn't happen. Just fall off to ROTATION_0
+                endCenterX = width
+                endCenterY = height * 0.5f
+                startCenterX = endCenterX + OFFSET
+                startCenterY = endCenterY
+                boxWidth = THICKNESS
+                boxHeight = height
+            }
+        }
+
+        return GlowBoxConfig(
+            startCenterX = startCenterX,
+            startCenterY = startCenterY,
+            endCenterX = endCenterX,
+            endCenterY = endCenterY,
+            width = boxWidth,
+            height = boxHeight,
+            color = Utils.getColorAttr(context, android.R.attr.colorAccent).defaultColor,
+            blurAmount = BLUR_AMOUNT,
+            duration = DURATION,
+            easeInDuration = EASE_DURATION,
+            easeOutDuration = EASE_DURATION
+        )
+    }
+
+    private companion object {
+        private const val OFFSET = 300f
+        private const val THICKNESS = 20f
+        private const val BLUR_AMOUNT = 700f
+        private const val DURATION = 3000L
+        private const val EASE_DURATION = 800L
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
new file mode 100644
index 0000000..52ccc21
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.ui.composable
+
+import androidx.annotation.StringRes
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.OpenInNew
+import androidx.compose.material.icons.filled.Accessibility
+import androidx.compose.material.icons.filled.Apps
+import androidx.compose.material.icons.filled.ExpandMore
+import androidx.compose.material.icons.filled.Keyboard
+import androidx.compose.material.icons.filled.Search
+import androidx.compose.material.icons.filled.Tv
+import androidx.compose.material.icons.filled.VerticalSplit
+import androidx.compose.material3.CenterAlignedTopAppBar
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.NavigationDrawerItemColors
+import androidx.compose.material3.NavigationDrawerItemDefaults
+import androidx.compose.material3.SearchBar
+import androidx.compose.material3.SearchBarDefaults
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.compose.ui.util.fastForEach
+import androidx.compose.ui.util.fastForEachIndexed
+import com.android.compose.windowsizeclass.LocalWindowSizeClass
+import com.android.systemui.res.R
+
+@Composable
+fun ShortcutHelper(modifier: Modifier = Modifier, onKeyboardSettingsClicked: () -> Unit) {
+    if (shouldUseSinglePane()) {
+        ShortcutHelperSinglePane(modifier, categories, onKeyboardSettingsClicked)
+    } else {
+        ShortcutHelperTwoPane(modifier, categories, onKeyboardSettingsClicked)
+    }
+}
+
+@Composable
+private fun shouldUseSinglePane() =
+    LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Compact
+
+@Composable
+private fun ShortcutHelperSinglePane(
+    modifier: Modifier = Modifier,
+    categories: List<ShortcutHelperCategory>,
+    onKeyboardSettingsClicked: () -> Unit,
+) {
+    Column(
+        modifier =
+            modifier
+                .fillMaxSize()
+                .verticalScroll(rememberScrollState())
+                .padding(start = 16.dp, end = 16.dp, top = 26.dp)
+    ) {
+        TitleBar()
+        Spacer(modifier = Modifier.height(6.dp))
+        ShortcutsSearchBar()
+        Spacer(modifier = Modifier.height(16.dp))
+        CategoriesPanelSinglePane(categories)
+        Spacer(modifier = Modifier.weight(1f))
+        KeyboardSettings(onClick = onKeyboardSettingsClicked)
+    }
+}
+
+@Composable
+private fun CategoriesPanelSinglePane(
+    categories: List<ShortcutHelperCategory>,
+) {
+    var expandedCategory by remember { mutableStateOf<ShortcutHelperCategory?>(null) }
+    Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
+        categories.fastForEachIndexed { index, category ->
+            val isExpanded = expandedCategory == category
+            val itemShape =
+                if (index == 0) {
+                    ShortcutHelper.Shapes.singlePaneFirstCategory
+                } else if (index == categories.lastIndex) {
+                    ShortcutHelper.Shapes.singlePaneLastCategory
+                } else {
+                    ShortcutHelper.Shapes.singlePaneCategory
+                }
+            CategoryItemSinglePane(
+                category = category,
+                isExpanded = isExpanded,
+                onClick = {
+                    expandedCategory =
+                        if (isExpanded) {
+                            null
+                        } else {
+                            category
+                        }
+                },
+                shape = itemShape,
+            )
+        }
+    }
+}
+
+@Composable
+private fun CategoryItemSinglePane(
+    category: ShortcutHelperCategory,
+    isExpanded: Boolean,
+    onClick: () -> Unit,
+    shape: Shape,
+) {
+    Surface(
+        color = MaterialTheme.colorScheme.surfaceBright,
+        shape = shape,
+        onClick = onClick,
+    ) {
+        Column {
+            Row(
+                verticalAlignment = Alignment.CenterVertically,
+                modifier = Modifier.fillMaxWidth().heightIn(min = 88.dp).padding(horizontal = 16.dp)
+            ) {
+                Icon(category.icon, contentDescription = null)
+                Spacer(modifier = Modifier.width(16.dp))
+                Text(stringResource(category.labelResId))
+                Spacer(modifier = Modifier.weight(1f))
+                RotatingExpandCollapseIcon(isExpanded)
+            }
+            AnimatedVisibility(visible = isExpanded) { ShortcutCategoryDetailsSinglePane(category) }
+        }
+    }
+}
+
+@Composable
+private fun RotatingExpandCollapseIcon(isExpanded: Boolean) {
+    val expandIconRotationDegrees by
+        animateFloatAsState(
+            targetValue =
+                if (isExpanded) {
+                    180f
+                } else {
+                    0f
+                },
+            label = "Expand icon rotation animation"
+        )
+    Icon(
+        modifier =
+            Modifier.background(
+                    color = MaterialTheme.colorScheme.surfaceContainerHigh,
+                    shape = CircleShape
+                )
+                .graphicsLayer { rotationZ = expandIconRotationDegrees },
+        imageVector = Icons.Default.ExpandMore,
+        contentDescription =
+            if (isExpanded) {
+                stringResource(R.string.shortcut_helper_content_description_collapse_icon)
+            } else {
+                stringResource(R.string.shortcut_helper_content_description_expand_icon)
+            },
+        tint = MaterialTheme.colorScheme.onSurface
+    )
+}
+
+@Composable
+private fun ShortcutCategoryDetailsSinglePane(category: ShortcutHelperCategory) {
+    Box(modifier = Modifier.fillMaxWidth().heightIn(min = 300.dp)) {
+        Text(
+            modifier = Modifier.align(Alignment.Center),
+            text = stringResource(category.labelResId),
+        )
+    }
+}
+
+@Composable
+private fun ShortcutHelperTwoPane(
+    modifier: Modifier = Modifier,
+    categories: List<ShortcutHelperCategory>,
+    onKeyboardSettingsClicked: () -> Unit,
+) {
+    Column(modifier = modifier.fillMaxSize().padding(start = 24.dp, end = 24.dp, top = 26.dp)) {
+        TitleBar()
+        Spacer(modifier = Modifier.height(12.dp))
+        Row(Modifier.fillMaxWidth()) {
+            StartSidePanel(
+                modifier = Modifier.fillMaxWidth(fraction = 0.32f),
+                categories = categories,
+                onKeyboardSettingsClicked = onKeyboardSettingsClicked,
+            )
+            Spacer(modifier = Modifier.width(24.dp))
+            EndSidePanel(Modifier.fillMaxSize())
+        }
+    }
+}
+
+@Composable
+private fun StartSidePanel(
+    modifier: Modifier,
+    categories: List<ShortcutHelperCategory>,
+    onKeyboardSettingsClicked: () -> Unit,
+) {
+    Column(modifier) {
+        ShortcutsSearchBar()
+        Spacer(modifier = Modifier.heightIn(16.dp))
+        CategoriesPanelTwoPane(categories)
+        Spacer(modifier = Modifier.weight(1f))
+        KeyboardSettings(onKeyboardSettingsClicked)
+    }
+}
+
+@Composable
+private fun CategoriesPanelTwoPane(categories: List<ShortcutHelperCategory>) {
+    var selected by remember { mutableStateOf(categories.first()) }
+    Column {
+        categories.fastForEach {
+            CategoryItemTwoPane(
+                label = stringResource(it.labelResId),
+                icon = it.icon,
+                selected = selected == it,
+                onClick = { selected = it }
+            )
+        }
+    }
+}
+
+@Composable
+private fun CategoryItemTwoPane(
+    label: String,
+    icon: ImageVector,
+    selected: Boolean,
+    onClick: () -> Unit,
+    colors: NavigationDrawerItemColors =
+        NavigationDrawerItemDefaults.colors(unselectedContainerColor = Color.Transparent),
+) {
+    Surface(
+        selected = selected,
+        onClick = onClick,
+        modifier = Modifier.semantics { role = Role.Tab }.heightIn(min = 72.dp).fillMaxWidth(),
+        shape = RoundedCornerShape(28.dp),
+        color = colors.containerColor(selected).value,
+    ) {
+        Row(Modifier.padding(horizontal = 24.dp), verticalAlignment = Alignment.CenterVertically) {
+            Icon(
+                modifier = Modifier.size(24.dp),
+                imageVector = icon,
+                contentDescription = null,
+                tint = colors.iconColor(selected).value
+            )
+            Spacer(Modifier.width(12.dp))
+            Box(Modifier.weight(1f)) {
+                Text(
+                    fontSize = 18.sp,
+                    color = colors.textColor(selected).value,
+                    style = MaterialTheme.typography.headlineSmall,
+                    text = label
+                )
+            }
+        }
+    }
+}
+
+@Composable
+fun EndSidePanel(modifier: Modifier) {
+    Surface(
+        modifier = modifier,
+        shape = RoundedCornerShape(28.dp),
+        color = MaterialTheme.colorScheme.surfaceBright
+    ) {}
+}
+
+@Composable
+@OptIn(ExperimentalMaterial3Api::class)
+private fun TitleBar() {
+    CenterAlignedTopAppBar(
+        colors = TopAppBarDefaults.centerAlignedTopAppBarColors(containerColor = Color.Transparent),
+        title = {
+            Text(
+                text = stringResource(R.string.shortcut_helper_title),
+                color = MaterialTheme.colorScheme.onSurface,
+                style = MaterialTheme.typography.headlineSmall
+            )
+        }
+    )
+}
+
+@Composable
+@OptIn(ExperimentalMaterial3Api::class)
+private fun ShortcutsSearchBar() {
+    var query by remember { mutableStateOf("") }
+    SearchBar(
+        colors = SearchBarDefaults.colors(containerColor = MaterialTheme.colorScheme.surfaceBright),
+        query = query,
+        active = false,
+        onActiveChange = {},
+        onQueryChange = { query = it },
+        onSearch = {},
+        leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
+        placeholder = { Text(text = stringResource(R.string.shortcut_helper_search_placeholder)) },
+        content = {}
+    )
+}
+
+@Composable
+private fun KeyboardSettings(onClick: () -> Unit) {
+    Surface(
+        onClick = onClick,
+        shape = RoundedCornerShape(24.dp),
+        color = Color.Transparent,
+        modifier = Modifier.semantics { role = Role.Button }.fillMaxWidth()
+    ) {
+        Row(
+            modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp),
+            verticalAlignment = Alignment.CenterVertically
+        ) {
+            Text(
+                "Keyboard Settings",
+                color = MaterialTheme.colorScheme.onSurfaceVariant,
+                fontSize = 16.sp
+            )
+            Spacer(modifier = Modifier.width(8.dp))
+            Icon(
+                imageVector = Icons.AutoMirrored.Default.OpenInNew,
+                contentDescription = null,
+                tint = MaterialTheme.colorScheme.onSurfaceVariant
+            )
+        }
+    }
+}
+
+/** Temporary data class just to populate the UI. */
+private data class ShortcutHelperCategory(
+    @StringRes val labelResId: Int,
+    val icon: ImageVector,
+)
+
+// Temporarily populating the categories directly in the UI.
+private val categories =
+    listOf(
+        ShortcutHelperCategory(R.string.shortcut_helper_category_system, Icons.Default.Tv),
+        ShortcutHelperCategory(
+            R.string.shortcut_helper_category_multitasking,
+            Icons.Default.VerticalSplit
+        ),
+        ShortcutHelperCategory(R.string.shortcut_helper_category_input, Icons.Default.Keyboard),
+        ShortcutHelperCategory(R.string.shortcut_helper_category_app_shortcuts, Icons.Default.Apps),
+        ShortcutHelperCategory(R.string.shortcut_helper_category_a11y, Icons.Default.Accessibility),
+    )
+
+object ShortcutHelper {
+
+    object Shapes {
+        val singlePaneFirstCategory =
+            RoundedCornerShape(
+                topStart = Dimensions.SinglePaneCategoryCornerRadius,
+                topEnd = Dimensions.SinglePaneCategoryCornerRadius
+            )
+        val singlePaneLastCategory =
+            RoundedCornerShape(
+                bottomStart = Dimensions.SinglePaneCategoryCornerRadius,
+                bottomEnd = Dimensions.SinglePaneCategoryCornerRadius
+            )
+        val singlePaneCategory = RectangleShape
+    }
+
+    object Dimensions {
+        val SinglePaneCategoryCornerRadius = 28.dp
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
index ef4156d..1e8d239 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
@@ -23,9 +23,12 @@
 import androidx.activity.BackEventCompat
 import androidx.activity.ComponentActivity
 import androidx.activity.OnBackPressedCallback
+import androidx.compose.ui.platform.ComposeView
 import androidx.core.view.updatePadding
 import androidx.lifecycle.flowWithLifecycle
 import androidx.lifecycle.lifecycleScope
+import com.android.compose.theme.PlatformTheme
+import com.android.systemui.keyboard.shortcut.ui.composable.ShortcutHelper
 import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutHelperViewModel
 import com.android.systemui.res.R
 import com.google.android.material.bottomsheet.BottomSheetBehavior
@@ -58,14 +61,30 @@
         super.onCreate(savedInstanceState)
         setContentView(R.layout.activity_keyboard_shortcut_helper)
         setUpBottomSheetWidth()
+        expandBottomSheet()
         setUpInsets()
         setUpPredictiveBack()
         setUpSheetDismissListener()
         setUpDismissOnTouchOutside()
+        setUpComposeView()
         observeFinishRequired()
         viewModel.onViewOpened()
     }
 
+    private fun setUpComposeView() {
+        requireViewById<ComposeView>(R.id.shortcut_helper_compose_container).apply {
+            setContent {
+                PlatformTheme {
+                    ShortcutHelper(
+                        onKeyboardSettingsClicked = ::onKeyboardSettingsClicked,
+                    )
+                }
+            }
+        }
+    }
+
+    private fun onKeyboardSettingsClicked() {}
+
     override fun onDestroy() {
         super.onDestroy()
         if (isFinishing) {
@@ -101,7 +120,8 @@
         bottomSheetContainer.setOnApplyWindowInsetsListener { _, insets ->
             val safeDrawingInsets = insets.safeDrawing
             // Make sure the bottom sheet is not covered by the status bar.
-            bottomSheetContainer.updatePadding(top = safeDrawingInsets.top)
+            bottomSheetBehavior.maxHeight =
+                resources.displayMetrics.heightPixels - safeDrawingInsets.top
             // Make sure the contents inside of the bottom sheet are not hidden by system bars, or
             // cutouts.
             bottomSheet.updatePadding(
@@ -171,7 +191,6 @@
 private val WindowInsets.safeDrawing
     get() =
         getInsets(WindowInsets.Type.systemBars())
-            .union(getInsets(WindowInsets.Type.ime()))
             .union(getInsets(WindowInsets.Type.displayCutout()))
 
 private fun Insets.union(insets: Insets): Insets = Insets.max(this, insets)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index c32c226..306f4ff 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -53,7 +53,6 @@
 import com.android.systemui.keyguard.ui.composable.blueprint.ComposableLockscreenSceneBlueprint
 import com.android.systemui.keyguard.ui.view.KeyguardIndicationArea
 import com.android.systemui.keyguard.ui.view.KeyguardRootView
-import com.android.systemui.keyguard.ui.view.layout.KeyguardBlueprintCommandListener
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
@@ -89,7 +88,6 @@
     private val screenOffAnimationController: ScreenOffAnimationController,
     private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
     private val chipbarCoordinator: ChipbarCoordinator,
-    private val keyguardBlueprintCommandListener: KeyguardBlueprintCommandListener,
     private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel,
     private val keyguardStatusViewComponentFactory: KeyguardStatusViewComponent.Factory,
     private val configuration: ConfigurationState,
@@ -105,7 +103,6 @@
     private val smartspaceViewModel: KeyguardSmartspaceViewModel,
     private val lockscreenContentViewModel: LockscreenContentViewModel,
     private val lockscreenSceneBlueprintsLazy: Lazy<Set<LockscreenSceneBlueprint>>,
-    private val keyguardBlueprintViewBinder: KeyguardBlueprintViewBinder,
     private val clockInteractor: KeyguardClockInteractor,
     private val keyguardViewMediator: KeyguardViewMediator,
 ) : CoreStartable {
@@ -152,7 +149,7 @@
                 cs.connect(composeView.id, BOTTOM, PARENT_ID, BOTTOM)
                 keyguardRootView.addView(composeView)
             } else {
-                keyguardBlueprintViewBinder.bind(
+                KeyguardBlueprintViewBinder.bind(
                     keyguardRootView,
                     keyguardBlueprintViewModel,
                     keyguardClockViewModel,
@@ -160,7 +157,6 @@
                 )
             }
         }
-        keyguardBlueprintCommandListener.start()
     }
 
     fun bindIndicationArea() {
@@ -200,12 +196,14 @@
             KeyguardRootViewBinder.bind(
                 keyguardRootView,
                 keyguardRootViewModel,
+                keyguardBlueprintViewModel,
                 configuration,
                 occludingAppDeviceEntryMessageViewModel,
                 chipbarCoordinator,
                 screenOffAnimationController,
                 shadeInteractor,
                 clockInteractor,
+                keyguardClockViewModel,
                 interactionJankMonitor,
                 deviceEntryHapticsInteractor,
                 vibratorHelper,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 81c2d92..f3a1843 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2380,7 +2380,10 @@
             }
             mCustomMessage = message;
             mKeyguardViewControllerLazy.get().dismissAndCollapse();
-        } else if (callback != null) {
+            return;
+        }
+        Log.w(TAG, "Ignoring request to DISMISS because mShowing=false");
+        if (callback != null) {
             new DismissCallbackWrapper(callback).notifyDismissError();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
index 3cbcb2c..97ea16d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ResourceTrimmer.kt
@@ -18,21 +18,15 @@
 
 import android.annotation.WorkerThread
 import android.content.ComponentCallbacks2
-import android.graphics.HardwareRenderer
-import android.os.Trace
 import android.util.Log
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
 import com.android.systemui.keyguard.shared.model.TransitionState
-import com.android.systemui.power.domain.interactor.PowerInteractor
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.Scenes
@@ -40,10 +34,7 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.launch
 
 /**
@@ -57,37 +48,15 @@
 class ResourceTrimmer
 @Inject
 constructor(
-    private val keyguardInteractor: KeyguardInteractor,
-    private val powerInteractor: PowerInteractor,
     private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
     private val globalWindowManager: GlobalWindowManager,
     @Application private val applicationScope: CoroutineScope,
     @Background private val bgDispatcher: CoroutineDispatcher,
-    private val featureFlags: FeatureFlags,
     private val sceneInteractor: SceneInteractor,
-) : CoreStartable, WakefulnessLifecycle.Observer {
+) : CoreStartable {
 
     override fun start() {
         Log.d(LOG_TAG, "Resource trimmer registered.")
-        if (com.android.systemui.Flags.trimResourcesWithBackgroundTrimAtLock()) {
-            applicationScope.launch(bgDispatcher) {
-                // We need to wait for the AoD transition (and animation) to complete.
-                // This means we're waiting for isDreaming (== implies isDoze) and dozeAmount == 1f
-                // signal. This is to make sure we don't clear font caches during animation which
-                // would jank and leave stale data in memory.
-                val isDozingFully =
-                    keyguardInteractor.dozeAmount.map { it == 1f }.distinctUntilChanged()
-                combine(
-                        powerInteractor.isAsleep,
-                        keyguardInteractor.isDreaming,
-                        isDozingFully,
-                        ::Triple
-                    )
-                    .distinctUntilChanged()
-                    .collect { onWakefulnessUpdated(it.first, it.second, it.third) }
-            }
-        }
-
         applicationScope.launch(bgDispatcher) {
             // We drop 1 to avoid triggering on initial collect().
             if (SceneContainerFlag.isEnabled) {
@@ -110,47 +79,9 @@
         // lockscreen elements, especially clocks.
         Log.d(LOG_TAG, "Sending TRIM_MEMORY_UI_HIDDEN.")
         globalWindowManager.trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
-        if (featureFlags.isEnabled(Flags.TRIM_FONT_CACHES_AT_UNLOCK)) {
-            if (DEBUG) {
-                Log.d(LOG_TAG, "Trimming font caches since keyguard went away.")
-            }
-            globalWindowManager.trimCaches(HardwareRenderer.CACHE_TRIM_FONT)
-        }
-    }
-
-    @WorkerThread
-    private fun onWakefulnessUpdated(
-        isAsleep: Boolean,
-        isDreaming: Boolean,
-        isDozingFully: Boolean
-    ) {
-        if (!com.android.systemui.Flags.trimResourcesWithBackgroundTrimAtLock()) {
-            return
-        }
-
-        if (DEBUG) {
-            Log.d(LOG_TAG, "isAsleep: $isAsleep Dreaming: $isDreaming DozeAmount: $isDozingFully")
-        }
-        // There are three scenarios:
-        // * No dozing and no AoD at all - where we go directly to ASLEEP with isDreaming = false
-        //      and dozeAmount == 0f
-        // * Dozing without Aod - where we go to ASLEEP with isDreaming = true and dozeAmount jumps
-        //      to 1f
-        // * AoD - where we go to ASLEEP with iDreaming = true and dozeAmount slowly increases
-        //      to 1f
-        val dozeDisabledAndScreenOff = isAsleep && !isDreaming
-        val dozeEnabledAndDozeAnimationCompleted = isAsleep && isDreaming && isDozingFully
-        if (dozeDisabledAndScreenOff || dozeEnabledAndDozeAnimationCompleted) {
-            Trace.beginSection("ResourceTrimmer#trimMemory")
-            Log.d(LOG_TAG, "SysUI asleep, trimming memory.")
-            globalWindowManager.trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
-            globalWindowManager.trimCaches(HardwareRenderer.CACHE_TRIM_ALL)
-            Trace.endSection()
-        }
     }
 
     companion object {
         private const val LOG_TAG = "ResourceTrimmer"
-        private val DEBUG = Log.isLoggable(LOG_TAG, Log.DEBUG)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
index 80675d3..0863cd7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/BuiltInKeyguardQuickAffordanceKeys.kt
@@ -28,6 +28,8 @@
     const val CREATE_NOTE = "create_note"
     const val DO_NOT_DISTURB = "do_not_disturb"
     const val FLASHLIGHT = "flashlight"
+    // TODO(b/339667383): delete or properly implement this once a product decision is made
+    const val GLANCEABLE_HUB = "glanceable_hub"
     const val HOME_CONTROLS = "home"
     const val MUTE = "mute"
     const val QR_CODE_SCANNER = "qr_code_scanner"
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
new file mode 100644
index 0000000..d09b9f6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/GlanceableHubQuickAffordanceConfig.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.quickaffordance
+
+import com.android.systemui.Flags
+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.communal.data.repository.CommunalSceneRepository
+import com.android.systemui.communal.shared.model.CommunalScenes
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flowOf
+
+/** Shortcut that opens the glanceable hub. */
+// TODO(b/339667383): delete or properly implement this once a product decision is made
+@SysUISingleton
+class GlanceableHubQuickAffordanceConfig
+@Inject
+constructor(
+    private val communalRepository: CommunalSceneRepository,
+) : KeyguardQuickAffordanceConfig {
+
+    override val key: String = BuiltInKeyguardQuickAffordanceKeys.GLANCEABLE_HUB
+    override fun pickerName(): String = "Glanceable hub"
+
+    override val pickerIconResourceId = R.drawable.ic_widgets
+
+    override val lockScreenState: Flow<KeyguardQuickAffordanceConfig.LockScreenState> by lazy {
+        if (Flags.glanceableHubShortcutButton()) {
+            val contentDescription = ContentDescription.Loaded(pickerName())
+            val icon = Icon.Resource(pickerIconResourceId, contentDescription)
+            flowOf(KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon))
+        } else {
+            flowOf(KeyguardQuickAffordanceConfig.LockScreenState.Hidden)
+        }
+    }
+
+    override fun onTriggered(
+        expandable: Expandable?
+    ): KeyguardQuickAffordanceConfig.OnTriggeredResult {
+        communalRepository.changeScene(CommunalScenes.Communal, null)
+        return KeyguardQuickAffordanceConfig.OnTriggeredResult.Handled
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
index 4556195..93296f0 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardDataQuickAffordanceModule.kt
@@ -36,6 +36,7 @@
             camera: CameraQuickAffordanceConfig,
             doNotDisturb: DoNotDisturbQuickAffordanceConfig,
             flashlight: FlashlightQuickAffordanceConfig,
+            glanceableHub: GlanceableHubQuickAffordanceConfig,
             home: HomeControlsKeyguardQuickAffordanceConfig,
             mute: MuteQuickAffordanceConfig,
             quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig,
@@ -46,6 +47,7 @@
                 camera,
                 doNotDisturb,
                 flashlight,
+                glanceableHub,
                 home,
                 mute,
                 quickAccessWallet,
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 deedbdb..0748979 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
@@ -20,15 +20,17 @@
 import android.content.Context
 import android.content.IntentFilter
 import android.content.SharedPreferences
-import com.android.systemui.res.R
+import com.android.systemui.Flags
 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.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.settings.SystemSettings
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.channels.awaitClose
@@ -50,6 +52,7 @@
     @Application private val context: Context,
     private val userFileManager: UserFileManager,
     private val userTracker: UserTracker,
+    private val systemSettings: SystemSettings,
     broadcastDispatcher: BroadcastDispatcher,
 ) : KeyguardQuickAffordanceSelectionManager {
 
@@ -70,6 +73,22 @@
     }
 
     private val defaults: Map<String, List<String>> by lazy {
+        // Quick hack to allow testing out a lock screen shortcut to open the glanceable hub. This
+        // flag will not be rolled out and is only used for local testing.
+        // TODO(b/339667383): delete or properly implement this once a product decision is made
+        if (Flags.glanceableHubShortcutButton()) {
+            if (systemSettings.getBool("open_hub_chip_replace_home_controls", false)) {
+                return@lazy mapOf(
+                    "bottom_start" to listOf("glanceable_hub"),
+                    "bottom_end" to listOf("create_note")
+                )
+            } else {
+                return@lazy mapOf(
+                    "bottom_start" to listOf("home"),
+                    "bottom_end" to listOf("glanceable_hub")
+                )
+            }
+        }
         context.resources
             .getStringArray(R.array.config_keyguardQuickAffordanceDefaults)
             .associate { item ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
index c11c49c..b826a00 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepository.kt
@@ -91,9 +91,9 @@
      */
     fun refreshBlueprint(config: Config = Config.DEFAULT) {
         fun scheduleCallback() {
-            // We use a handler here instead of a CoroutineDipsatcher because the one provided by
+            // We use a handler here instead of a CoroutineDispatcher because the one provided by
             // @Main CoroutineDispatcher is currently Dispatchers.Main.immediate, which doesn't
-            // delay the callback, and instead runs it imemdiately.
+            // delay the callback, and instead runs it immediately.
             handler.post {
                 assert.isMainThread()
                 targetTransitionConfig?.let {
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 a2bbcad..f15896b 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
@@ -183,7 +183,7 @@
     val statusBarState: StateFlow<StatusBarState>
 
     /** Observable for biometric unlock state which includes the mode and unlock source */
-    val biometricUnlockState: Flow<BiometricUnlockModel>
+    val biometricUnlockState: StateFlow<BiometricUnlockModel>
 
     fun setBiometricUnlockState(
         unlockMode: BiometricUnlockMode,
@@ -307,17 +307,20 @@
     private val _dismissAction: MutableStateFlow<DismissAction> =
         MutableStateFlow(DismissAction.None)
     override val dismissAction = _dismissAction.asStateFlow()
+
     override fun setDismissAction(dismissAction: DismissAction) {
         _dismissAction.value = dismissAction
     }
 
     private val _keyguardDone: MutableSharedFlow<KeyguardDone> = MutableSharedFlow()
     override val keyguardDone = _keyguardDone.asSharedFlow()
+
     override suspend fun setKeyguardDone(keyguardDoneType: KeyguardDone) {
         _keyguardDone.emit(keyguardDoneType)
     }
 
     override val keyguardDoneAnimationsFinished: MutableSharedFlow<Unit> = MutableSharedFlow()
+
     override fun keyguardDoneAnimationsFinished() {
         keyguardDoneAnimationsFinished.tryEmit(Unit)
     }
@@ -490,6 +493,7 @@
                         override fun onStartDream() {
                             trySendWithFailureLogging(true, TAG, "updated isDreamingWithOverlay")
                         }
+
                         override fun onWakeUp() {
                             trySendWithFailureLogging(false, TAG, "updated isDreamingWithOverlay")
                         }
@@ -618,7 +622,8 @@
 
     private val _biometricUnlockState: MutableStateFlow<BiometricUnlockModel> =
         MutableStateFlow(BiometricUnlockModel(BiometricUnlockMode.NONE, null))
-    override val biometricUnlockState = _biometricUnlockState.asStateFlow()
+    override val biometricUnlockState: StateFlow<BiometricUnlockModel> =
+        _biometricUnlockState.asStateFlow()
 
     override fun setBiometricUnlockState(
         unlockMode: BiometricUnlockMode,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index f488d3b..8ec460a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -265,7 +265,7 @@
         state: TransitionState
     ) {
         if (updateTransitionId != transitionId) {
-            Log.w(TAG, "Attempting to update with old/invalid transitionId: $transitionId")
+            Log.wtf(TAG, "Attempting to update with old/invalid transitionId: $transitionId")
             return
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricUnlockInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricUnlockInteractor.kt
index 576fafd..ebc3483 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricUnlockInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BiometricUnlockInteractor.kt
@@ -3,6 +3,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
 import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
 import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_DISMISS_BOUNCER
 import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_NONE
@@ -15,6 +16,7 @@
 import com.android.systemui.statusbar.phone.BiometricUnlockController.WakeAndUnlockMode
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.StateFlow
 
 @ExperimentalCoroutinesApi
 @SysUISingleton
@@ -24,6 +26,8 @@
     private val keyguardRepository: KeyguardRepository,
 ) {
 
+    val unlockState: StateFlow<BiometricUnlockModel> = keyguardRepository.biometricUnlockState
+
     fun setBiometricUnlockState(
         @WakeAndUnlockMode unlockStateInt: Int,
         biometricUnlockSource: BiometricUnlockSource?,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index 9b07675f..756c6c2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -57,7 +57,7 @@
     @Background private val scope: CoroutineScope,
     @Background bgDispatcher: CoroutineDispatcher,
     @Main mainDispatcher: CoroutineDispatcher,
-    private val keyguardInteractor: KeyguardInteractor,
+    keyguardInteractor: KeyguardInteractor,
     private val communalInteractor: CommunalInteractor,
     powerInteractor: PowerInteractor,
     keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
@@ -70,6 +70,7 @@
         bgDispatcher = bgDispatcher,
         powerInteractor = powerInteractor,
         keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+        keyguardInteractor = keyguardInteractor,
     ) {
 
     override fun start() {
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 01109af..2a9ee9f 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
@@ -48,7 +48,7 @@
     @Background private val scope: CoroutineScope,
     @Background bgDispatcher: CoroutineDispatcher,
     @Main mainDispatcher: CoroutineDispatcher,
-    private val keyguardInteractor: KeyguardInteractor,
+    keyguardInteractor: KeyguardInteractor,
     powerInteractor: PowerInteractor,
     keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
     val deviceEntryRepository: DeviceEntryRepository,
@@ -60,6 +60,7 @@
         bgDispatcher = bgDispatcher,
         powerInteractor = powerInteractor,
         keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+        keyguardInteractor = keyguardInteractor,
     ) {
 
     override fun start() {
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 7d3de30..f5e98f1 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
@@ -47,7 +47,7 @@
     @Background private val scope: CoroutineScope,
     @Background bgDispatcher: CoroutineDispatcher,
     @Main mainDispatcher: CoroutineDispatcher,
-    private val keyguardInteractor: KeyguardInteractor,
+    keyguardInteractor: KeyguardInteractor,
     powerInteractor: PowerInteractor,
     private val communalInteractor: CommunalInteractor,
     keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
@@ -60,6 +60,7 @@
         bgDispatcher = bgDispatcher,
         powerInteractor = powerInteractor,
         keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+        keyguardInteractor = keyguardInteractor,
     ) {
 
     override fun start() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
index 63294f7..47aa02a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
@@ -45,7 +45,7 @@
     @Background private val scope: CoroutineScope,
     @Background bgDispatcher: CoroutineDispatcher,
     @Main mainDispatcher: CoroutineDispatcher,
-    private val keyguardInteractor: KeyguardInteractor,
+    keyguardInteractor: KeyguardInteractor,
     powerInteractor: PowerInteractor,
     keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
 ) :
@@ -56,6 +56,7 @@
         bgDispatcher = bgDispatcher,
         powerInteractor = powerInteractor,
         keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+        keyguardInteractor = keyguardInteractor,
     ) {
 
     override fun start() {
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 7961b45..25c3b0d 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
@@ -51,7 +51,7 @@
     @Background private val scope: CoroutineScope,
     @Background bgDispatcher: CoroutineDispatcher,
     @Main mainDispatcher: CoroutineDispatcher,
-    private val keyguardInteractor: KeyguardInteractor,
+    keyguardInteractor: KeyguardInteractor,
     private val glanceableHubTransitions: GlanceableHubTransitions,
     powerInteractor: PowerInteractor,
     keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
@@ -63,6 +63,7 @@
         bgDispatcher = bgDispatcher,
         powerInteractor = powerInteractor,
         keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+        keyguardInteractor = keyguardInteractor,
     ) {
 
     override fun start() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
index ca6ab3e..e516fa3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
@@ -48,7 +48,7 @@
     @Main mainDispatcher: CoroutineDispatcher,
     @Background bgDispatcher: CoroutineDispatcher,
     private val glanceableHubTransitions: GlanceableHubTransitions,
-    private val keyguardInteractor: KeyguardInteractor,
+    keyguardInteractor: KeyguardInteractor,
     override val transitionRepository: KeyguardTransitionRepository,
     transitionInteractor: KeyguardTransitionInteractor,
     powerInteractor: PowerInteractor,
@@ -61,6 +61,7 @@
         bgDispatcher = bgDispatcher,
         powerInteractor = powerInteractor,
         keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+        keyguardInteractor = keyguardInteractor,
     ) {
 
     override fun start() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index 8ca29c8..a540d76 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -49,7 +49,7 @@
     @Background private val scope: CoroutineScope,
     @Background bgDispatcher: CoroutineDispatcher,
     @Main mainDispatcher: CoroutineDispatcher,
-    private val keyguardInteractor: KeyguardInteractor,
+    keyguardInteractor: KeyguardInteractor,
     powerInteractor: PowerInteractor,
     private val communalInteractor: CommunalInteractor,
     keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
@@ -64,6 +64,7 @@
         bgDispatcher = bgDispatcher,
         powerInteractor = powerInteractor,
         keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+        keyguardInteractor = keyguardInteractor,
     ) {
 
     override fun start() {
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 f1e98f3..8cab3cd 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
@@ -58,7 +58,7 @@
     @Background private val scope: CoroutineScope,
     @Background bgDispatcher: CoroutineDispatcher,
     @Main mainDispatcher: CoroutineDispatcher,
-    private val keyguardInteractor: KeyguardInteractor,
+    keyguardInteractor: KeyguardInteractor,
     private val flags: FeatureFlags,
     private val shadeRepository: ShadeRepository,
     powerInteractor: PowerInteractor,
@@ -73,6 +73,7 @@
         bgDispatcher = bgDispatcher,
         powerInteractor = powerInteractor,
         keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+        keyguardInteractor = keyguardInteractor,
     ) {
 
     override fun start() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index 2603aab2..86d4cfb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -45,7 +45,7 @@
     @Background private val scope: CoroutineScope,
     @Background bgDispatcher: CoroutineDispatcher,
     @Main mainDispatcher: CoroutineDispatcher,
-    private val keyguardInteractor: KeyguardInteractor,
+    keyguardInteractor: KeyguardInteractor,
     powerInteractor: PowerInteractor,
     private val communalInteractor: CommunalInteractor,
     keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
@@ -57,6 +57,7 @@
         bgDispatcher = bgDispatcher,
         powerInteractor = powerInteractor,
         keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+        keyguardInteractor = keyguardInteractor,
     ) {
 
     override fun start() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 53a0c32..19b2b81 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -52,7 +52,7 @@
     @Background private val scope: CoroutineScope,
     @Background bgDispatcher: CoroutineDispatcher,
     @Main mainDispatcher: CoroutineDispatcher,
-    private val keyguardInteractor: KeyguardInteractor,
+    keyguardInteractor: KeyguardInteractor,
     private val communalInteractor: CommunalInteractor,
     private val flags: FeatureFlags,
     private val keyguardSecurityModel: KeyguardSecurityModel,
@@ -67,6 +67,7 @@
         bgDispatcher = bgDispatcher,
         powerInteractor = powerInteractor,
         keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+        keyguardInteractor = keyguardInteractor,
     ) {
 
     override fun start() {
@@ -86,7 +87,7 @@
                     return@combine null
                 }
 
-                fromBouncerStep.value > 0.5f
+                fromBouncerStep.value > TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD
             }
             .onStart {
                 // Default to null ("don't care, use a reasonable default").
@@ -232,5 +233,6 @@
         val TO_AOD_DURATION = DEFAULT_DURATION
         val TO_LOCKSCREEN_DURATION = DEFAULT_DURATION
         val TO_DOZING_DURATION = DEFAULT_DURATION
+        val TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD = 0.5f
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
index 7cee258..41c3959 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
@@ -20,6 +20,7 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.content.Context
+import com.android.systemui.CoreStartable
 import com.android.systemui.biometrics.domain.interactor.FingerprintPropertyInteractor
 import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
 import com.android.systemui.dagger.SysUISingleton
@@ -31,17 +32,17 @@
 import com.android.systemui.keyguard.ui.view.layout.blueprints.SplitShadeKeyguardBlueprint
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Type
+import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection
+import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
 import com.android.systemui.shade.shared.model.ShadeMode
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.launch
 
 @SysUISingleton
@@ -53,9 +54,11 @@
     private val context: Context,
     private val shadeInteractor: ShadeInteractor,
     private val clockInteractor: KeyguardClockInteractor,
-    configurationInteractor: ConfigurationInteractor,
-    fingerprintPropertyInteractor: FingerprintPropertyInteractor,
-) {
+    private val configurationInteractor: ConfigurationInteractor,
+    private val fingerprintPropertyInteractor: FingerprintPropertyInteractor,
+    private val smartspaceSection: SmartspaceSection,
+    private val clockSection: ClockSection,
+) : CoreStartable {
     /** The current blueprint for the lockscreen. */
     val blueprint: StateFlow<KeyguardBlueprint> = keyguardBlueprintRepository.blueprint
 
@@ -75,15 +78,23 @@
             }
         }
 
-    private val refreshEvents: Flow<Unit> =
-        merge(
-            configurationInteractor.onAnyConfigurationChange,
-            fingerprintPropertyInteractor.propertiesInitialized.filter { it }.map {},
-        )
-
-    init {
+    override fun start() {
         applicationScope.launch { blueprintId.collect { transitionToBlueprint(it) } }
-        applicationScope.launch { refreshEvents.collect { refreshBlueprint() } }
+        applicationScope.launch {
+            fingerprintPropertyInteractor.propertiesInitialized
+                .filter { it }
+                .collect { refreshBlueprint() }
+        }
+        applicationScope.launch {
+            val refreshConfig =
+                Config(
+                    Type.NoTransition,
+                    rebuildSections = listOf(smartspaceSection),
+                )
+            configurationInteractor.onAnyConfigurationChange.collect {
+                refreshBlueprint(refreshConfig)
+            }
+        }
     }
 
     /**
@@ -120,4 +131,8 @@
     fun getCurrentBlueprint(): KeyguardBlueprint {
         return keyguardBlueprintRepository.blueprint.value
     }
+
+    companion object {
+        private val TAG = "KeyguardBlueprintInteractor"
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index c44a40f..73835a3c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -37,6 +37,7 @@
 import com.android.systemui.keyguard.shared.model.DozeStateModel
 import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff
 import com.android.systemui.keyguard.shared.model.DozeTransitionModel
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
 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.StatusBarState
@@ -47,8 +48,10 @@
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.data.repository.ShadeRepository
 import com.android.systemui.statusbar.CommandQueue
+import com.android.systemui.statusbar.notification.NotificationUtils.interpolate
 import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor
 import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
+import com.android.systemui.util.kotlin.pairwise
 import com.android.systemui.util.kotlin.sample
 import javax.inject.Inject
 import javax.inject.Provider
@@ -66,7 +69,9 @@
 import kotlinx.coroutines.flow.debounce
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
@@ -96,17 +101,54 @@
     // TODO(b/296118689): move to a repository
     private val _notificationPlaceholderBounds = MutableStateFlow(NotificationContainerBounds())
 
+    // When going to AOD, we interpolate bounds when receiving the new bounds
+    // When going back to LS, we'll apply new bounds directly
+    private val _nonSplitShadeNotifciationPlaceholderBounds =
+        _notificationPlaceholderBounds.pairwise().flatMapLatest { (oldBounds, newBounds) ->
+            val lastChangeStep = keyguardTransitionInteractor.transitionState.first()
+            if (lastChangeStep.to == AOD) {
+                keyguardTransitionInteractor.transitionState.map { step ->
+                    val startingProgress = lastChangeStep.value
+                    val progress = step.value
+                    if (step.to == AOD && progress >= startingProgress) {
+                        val adjustedProgress =
+                            ((progress - startingProgress) / (1F - startingProgress)).coerceIn(
+                                0F,
+                                1F
+                            )
+                        val top = interpolate(oldBounds.top, newBounds.top, adjustedProgress)
+                        val bottom =
+                            interpolate(
+                                oldBounds.bottom,
+                                newBounds.bottom,
+                                adjustedProgress.coerceIn(0F, 1F)
+                            )
+                        NotificationContainerBounds(top = top, bottom = bottom)
+                    } else {
+                        newBounds
+                    }
+                }
+            } else {
+                flow { emit(newBounds) }
+            }
+        }
+
     /** Bounds of the notification container. */
     val notificationContainerBounds: StateFlow<NotificationContainerBounds> by lazy {
         SceneContainerFlag.assertInLegacyMode()
         combine(
                 _notificationPlaceholderBounds,
+                _nonSplitShadeNotifciationPlaceholderBounds,
                 sharedNotificationContainerInteractor.get().configurationBasedDimensions,
-            ) { bounds, cfg ->
+            ) { bounds, nonSplitShadeBounds: NotificationContainerBounds, cfg ->
                 // We offset the placeholder bounds by the configured top margin to account for
                 // legacy placement behavior within notifications for splitshade.
-                if (MigrateClocksToBlueprint.isEnabled && cfg.useSplitShade) {
-                    bounds.copy(bottom = bounds.bottom - cfg.keyguardSplitShadeTopMargin)
+                if (MigrateClocksToBlueprint.isEnabled) {
+                    if (cfg.useSplitShade) {
+                        bounds.copy(bottom = bounds.bottom - cfg.keyguardSplitShadeTopMargin)
+                    } else {
+                        nonSplitShadeBounds
+                    }
                 } else bounds
             }
             .stateIn(
@@ -139,6 +181,8 @@
     /** Doze transition information. */
     val dozeTransitionModel: Flow<DozeTransitionModel> = repository.dozeTransitionModel
 
+    val isPulsing: Flow<Boolean> = dozeTransitionModel.map { it.to == DozeStateModel.DOZE_PULSING }
+
     /**
      * Whether the system is dreaming. [isDreaming] will be always be true when [isDozing] is true,
      * but not vice-versa.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index 323ceef..e148207 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -53,6 +53,7 @@
     val bgDispatcher: CoroutineDispatcher,
     val powerInteractor: PowerInteractor,
     val keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
+    val keyguardInteractor: KeyguardInteractor,
 ) {
     val name = this::class.simpleName ?: "UnknownTransitionInteractor"
     abstract val transitionRepository: KeyguardTransitionRepository
@@ -164,14 +165,10 @@
     @Deprecated("Will be merged into maybeStartTransitionToOccludedOrInsecureCamera")
     suspend fun maybeHandleInsecurePowerGesture(): Boolean {
         if (keyguardOcclusionInteractor.shouldTransitionFromPowerButtonGesture()) {
-            if (transitionInteractor.getCurrentState() == KeyguardState.GONE) {
-                // If the current state is GONE when the launch gesture is triggered, it means we
-                // were in transition from GONE -> DOZING/AOD due to the first power button tap. The
-                // second tap indicates that the user's intent was actually to launch the unlocked
-                // (insecure) camera, so we should transition back to GONE.
+            if (keyguardInteractor.isKeyguardDismissible.value) {
                 startTransitionTo(
                     KeyguardState.GONE,
-                    ownerReason = "Power button gesture while GONE"
+                    ownerReason = "Power button gesture while keyguard is dismissible"
                 )
 
                 return true
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index fb65a6d..88e6602 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -18,6 +18,7 @@
 
 package com.android.systemui.keyguard.domain.interactor
 
+import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
@@ -29,6 +30,7 @@
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
 import com.android.systemui.util.kotlin.sample
+import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
 import dagger.Lazy
 import javax.inject.Inject
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -84,25 +86,52 @@
             }
             .distinctUntilChanged()
 
+    private val isDeviceEntered: Flow<Boolean> by lazy {
+        deviceEntryInteractor.get().isDeviceEntered
+    }
+
+    private val isDeviceNotEntered: Flow<Boolean> by lazy { isDeviceEntered.map { !it } }
+
     /**
-     * Surface visibility, which is either determined by the default visibility in the FINISHED
-     * KeyguardState, or the transition-specific visibility used during certain RUNNING transitions.
+     * Surface visibility, which is either determined by the default visibility when not
+     * transitioning between [KeyguardState]s or [Scenes] or the transition-specific visibility used
+     * during certain ongoing transitions.
      */
     @OptIn(ExperimentalCoroutinesApi::class)
     val surfaceBehindVisibility: Flow<Boolean> =
-        transitionInteractor.isInTransitionToAnyState
-            .flatMapLatest { isInTransition ->
-                if (!isInTransition) {
-                    defaultSurfaceBehindVisibility
-                } else {
-                    combine(
-                        transitionSpecificSurfaceBehindVisibility,
-                        defaultSurfaceBehindVisibility,
-                    ) { transitionVisibility, defaultVisibility ->
-                        // Defer to the transition-specific visibility since we're RUNNING a
-                        // transition, but fall back to the default visibility if the current
-                        // transition's interactor did not specify a visibility.
-                        transitionVisibility ?: defaultVisibility
+        if (SceneContainerFlag.isEnabled) {
+                sceneInteractor.get().transitionState.flatMapLatestConflated { transitionState ->
+                    when (transitionState) {
+                        is ObservableTransitionState.Transition ->
+                            when {
+                                transitionState.fromScene == Scenes.Lockscreen &&
+                                    transitionState.toScene == Scenes.Gone -> flowOf(true)
+                                transitionState.fromScene == Scenes.Bouncer &&
+                                    transitionState.toScene == Scenes.Gone ->
+                                    transitionState.progress.map { progress ->
+                                        progress >
+                                            FromPrimaryBouncerTransitionInteractor
+                                                .TO_GONE_SURFACE_BEHIND_VISIBLE_THRESHOLD
+                                    }
+                                else -> isDeviceEntered
+                            }
+                        is ObservableTransitionState.Idle -> isDeviceEntered
+                    }
+                }
+            } else {
+                transitionInteractor.isInTransitionToAnyState.flatMapLatest { isInTransition ->
+                    if (!isInTransition) {
+                        defaultSurfaceBehindVisibility
+                    } else {
+                        combine(
+                            transitionSpecificSurfaceBehindVisibility,
+                            defaultSurfaceBehindVisibility,
+                        ) { transitionVisibility, defaultVisibility ->
+                            // Defer to the transition-specific visibility since we're RUNNING a
+                            // transition, but fall back to the default visibility if the current
+                            // transition's interactor did not specify a visibility.
+                            transitionVisibility ?: defaultVisibility
+                        }
                     }
                 }
             }
@@ -162,7 +191,7 @@
      */
     val lockscreenVisibility: Flow<Boolean> =
         if (SceneContainerFlag.isEnabled) {
-            deviceEntryInteractor.get().isDeviceEntered.map { !it }
+            isDeviceNotEntered
         } else {
             transitionInteractor.currentKeyguardState
                 .sample(transitionInteractor.startedStepWithPrecedingStep, ::Pair)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
index 7ca2eba..f5b82cf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
@@ -37,21 +37,41 @@
     fun replaceViews(
         constraintLayout: ConstraintLayout,
         previousBlueprint: KeyguardBlueprint? = null,
+        rebuildSections: List<KeyguardSection> = listOf(),
         bindData: Boolean = true
     ) {
-        val prevSections =
-            previousBlueprint?.let { prev ->
-                prev.sections.subtract(sections).forEach { it.removeViews(constraintLayout) }
-                prev.sections
-            }
-                ?: listOf()
-
-        sections.subtract(prevSections).forEach {
+        rebuildSections.forEach { it.onRebuildBegin() }
+        val prevSections = previousBlueprint?.sections ?: listOf()
+        val skipSections = sections.intersect(prevSections).subtract(rebuildSections)
+        prevSections.subtract(skipSections).forEach { it.removeViews(constraintLayout) }
+        sections.subtract(skipSections).forEach {
             it.addViews(constraintLayout)
             if (bindData) {
                 it.bindData(constraintLayout)
             }
         }
+        rebuildSections.forEach { it.onRebuildEnd() }
+    }
+
+    /** Rebuilds views for the target sections, or all of them if unspecified. */
+    fun rebuildViews(
+        constraintLayout: ConstraintLayout,
+        rebuildSections: List<KeyguardSection> = sections,
+        bindData: Boolean = true
+    ) {
+        if (rebuildSections.isEmpty()) {
+            return
+        }
+
+        rebuildSections.forEach { it.onRebuildBegin() }
+        rebuildSections.forEach { it.removeViews(constraintLayout) }
+        rebuildSections.forEach {
+            it.addViews(constraintLayout)
+            if (bindData) {
+                it.bindData(constraintLayout)
+            }
+        }
+        rebuildSections.forEach { it.onRebuildEnd() }
     }
 
     fun applyConstraints(constraintSet: ConstraintSet) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt
index 48a2146..473b08f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt
@@ -33,6 +33,12 @@
     /** Removes views and does any data binding destruction. */
     abstract fun removeViews(constraintLayout: ConstraintLayout)
 
+    /* Notifies the section is being rebuilt */
+    open fun onRebuildBegin() {}
+
+    /* Notifies the secion that the rebuild is complete */
+    open fun onRebuildEnd() {}
+
     /**
      * Defines equality as same class.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
index 52d7519..bec8f3d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinder.kt
@@ -17,17 +17,12 @@
 
 package com.android.systemui.keyguard.ui.binder
 
-import android.os.Handler
-import android.transition.Transition
-import android.transition.TransitionManager
 import android.util.Log
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.app.tracing.coroutines.launch
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.KeyguardBottomAreaRefactor
 import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
 import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.BaseBlueprintTransition
@@ -40,47 +35,9 @@
 import com.android.systemui.res.R
 import com.android.systemui.shared.R as sharedR
 import com.android.systemui.util.kotlin.pairwise
-import javax.inject.Inject
-import kotlin.math.max
 
-@SysUISingleton
-class KeyguardBlueprintViewBinder
-@Inject
-constructor(
-    @Main private val handler: Handler,
-) {
-    private var runningPriority = -1
-    private val runningTransitions = mutableSetOf<Transition>()
-    private val isTransitionRunning: Boolean
-        get() = runningTransitions.size > 0
-    private val transitionListener =
-        object : Transition.TransitionListener {
-            override fun onTransitionCancel(transition: Transition) {
-                if (DEBUG) Log.e(TAG, "onTransitionCancel: ${transition::class.simpleName}")
-                runningTransitions.remove(transition)
-            }
-
-            override fun onTransitionEnd(transition: Transition) {
-                if (DEBUG) Log.e(TAG, "onTransitionEnd: ${transition::class.simpleName}")
-                runningTransitions.remove(transition)
-            }
-
-            override fun onTransitionPause(transition: Transition) {
-                if (DEBUG) Log.i(TAG, "onTransitionPause: ${transition::class.simpleName}")
-                runningTransitions.remove(transition)
-            }
-
-            override fun onTransitionResume(transition: Transition) {
-                if (DEBUG) Log.i(TAG, "onTransitionResume: ${transition::class.simpleName}")
-                runningTransitions.add(transition)
-            }
-
-            override fun onTransitionStart(transition: Transition) {
-                if (DEBUG) Log.i(TAG, "onTransitionStart: ${transition::class.simpleName}")
-                runningTransitions.add(transition)
-            }
-        }
-
+object KeyguardBlueprintViewBinder {
+    @JvmStatic
     fun bind(
         constraintLayout: ConstraintLayout,
         viewModel: KeyguardBlueprintViewModel,
@@ -95,17 +52,8 @@
                             null as KeyguardBlueprint?,
                         )
                         .collect { (prevBlueprint, blueprint) ->
-                            val cs =
-                                ConstraintSet().apply {
-                                    clone(constraintLayout)
-                                    val emptyLayout = ConstraintSet.Layout()
-                                    knownIds.forEach {
-                                        getConstraint(it).layout.copyFrom(emptyLayout)
-                                    }
-                                    blueprint.applyConstraints(this)
-                                }
-
-                            var transition =
+                            val config = Config.DEFAULT
+                            val transition =
                                 if (
                                     !KeyguardBottomAreaRefactor.isEnabled &&
                                         prevBlueprint != null &&
@@ -114,23 +62,37 @@
                                     BaseBlueprintTransition(clockViewModel)
                                         .addTransition(
                                             IntraBlueprintTransition(
-                                                Config.DEFAULT,
+                                                config,
                                                 clockViewModel,
                                                 smartspaceViewModel
                                             )
                                         )
                                 } else {
                                     IntraBlueprintTransition(
-                                        Config.DEFAULT,
+                                        config,
                                         clockViewModel,
                                         smartspaceViewModel
                                     )
                                 }
 
-                            runTransition(constraintLayout, transition, Config.DEFAULT) {
-                                // Add and remove views of sections that are not contained by the
-                                // other.
-                                blueprint.replaceViews(constraintLayout, prevBlueprint)
+                            viewModel.runTransition(constraintLayout, transition, config) {
+                                // Replace sections from the previous blueprint with the new ones
+                                blueprint.replaceViews(
+                                    constraintLayout,
+                                    prevBlueprint,
+                                    config.rebuildSections
+                                )
+
+                                val cs =
+                                    ConstraintSet().apply {
+                                        clone(constraintLayout)
+                                        val emptyLayout = ConstraintSet.Layout()
+                                        knownIds.forEach {
+                                            getConstraint(it).layout.copyFrom(emptyLayout)
+                                        }
+                                        blueprint.applyConstraints(this)
+                                    }
+
                                 logAlphaVisibilityOfAppliedConstraintSet(cs, clockViewModel)
                                 cs.applyTo(constraintLayout)
                             }
@@ -138,22 +100,21 @@
                 }
 
                 launch("$TAG#viewModel.refreshTransition") {
-                    viewModel.refreshTransition.collect { transition ->
-                        val cs =
-                            ConstraintSet().apply {
-                                clone(constraintLayout)
-                                viewModel.blueprint.value.applyConstraints(this)
-                            }
+                    viewModel.refreshTransition.collect { config ->
+                        val blueprint = viewModel.blueprint.value
 
-                        runTransition(
+                        viewModel.runTransition(
                             constraintLayout,
-                            IntraBlueprintTransition(
-                                transition,
-                                clockViewModel,
-                                smartspaceViewModel
-                            ),
-                            transition,
+                            IntraBlueprintTransition(config, clockViewModel, smartspaceViewModel),
+                            config,
                         ) {
+                            blueprint.rebuildViews(constraintLayout, config.rebuildSections)
+
+                            val cs =
+                                ConstraintSet().apply {
+                                    clone(constraintLayout)
+                                    blueprint.applyConstraints(this)
+                                }
                             logAlphaVisibilityOfAppliedConstraintSet(cs, clockViewModel)
                             cs.applyTo(constraintLayout)
                         }
@@ -163,50 +124,6 @@
         }
     }
 
-    private fun runTransition(
-        constraintLayout: ConstraintLayout,
-        transition: Transition,
-        config: Config,
-        apply: () -> Unit,
-    ) {
-        val currentPriority = if (isTransitionRunning) runningPriority else -1
-        if (config.checkPriority && config.type.priority < currentPriority) {
-            if (DEBUG) {
-                Log.w(
-                    TAG,
-                    "runTransition: skipping ${transition::class.simpleName}: " +
-                        "currentPriority=$currentPriority; config=$config"
-                )
-            }
-            apply()
-            return
-        }
-
-        if (DEBUG) {
-            Log.i(
-                TAG,
-                "runTransition: running ${transition::class.simpleName}: " +
-                    "currentPriority=$currentPriority; config=$config"
-            )
-        }
-
-        // beginDelayedTransition makes a copy, so we temporarially add the uncopied transition to
-        // the running set until the copy is started by the handler.
-        runningTransitions.add(transition)
-        transition.addListener(transitionListener)
-        runningPriority = max(currentPriority, config.type.priority)
-
-        handler.post {
-            if (config.terminatePrevious) {
-                TransitionManager.endTransitions(constraintLayout)
-            }
-
-            TransitionManager.beginDelayedTransition(constraintLayout, transition)
-            runningTransitions.remove(transition)
-            apply()
-        }
-    }
-
     private fun logAlphaVisibilityOfAppliedConstraintSet(
         cs: ConstraintSet,
         viewModel: KeyguardClockViewModel
@@ -233,8 +150,6 @@
         )
     }
 
-    companion object {
-        private const val TAG = "KeyguardBlueprintViewBinder"
-        private const val DEBUG = false
-    }
+    private const val TAG = "KeyguardBlueprintViewBinder"
+    private const val DEBUG = false
 }
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 39db22d..fc92afe 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
@@ -22,6 +22,7 @@
 import android.annotation.SuppressLint
 import android.graphics.Point
 import android.graphics.Rect
+import android.util.Log
 import android.view.HapticFeedbackConstants
 import android.view.View
 import android.view.View.OnLayoutChangeListener
@@ -56,8 +57,11 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.keyguard.ui.viewmodel.OccludingAppDeviceEntryMessageViewModel
+import com.android.systemui.keyguard.ui.viewmodel.TransitionData
 import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.FalsingManager
@@ -93,12 +97,14 @@
     fun bind(
         view: ViewGroup,
         viewModel: KeyguardRootViewModel,
+        blueprintViewModel: KeyguardBlueprintViewModel,
         configuration: ConfigurationState,
         occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel?,
         chipbarCoordinator: ChipbarCoordinator?,
         screenOffAnimationController: ScreenOffAnimationController,
         shadeInteractor: ShadeInteractor,
         clockInteractor: KeyguardClockInteractor,
+        clockViewModel: KeyguardClockViewModel,
         interactionJankMonitor: InteractionJankMonitor?,
         deviceEntryHapticsInteractor: DeviceEntryHapticsInteractor?,
         vibratorHelper: VibratorHelper?,
@@ -348,7 +354,16 @@
             }
         }
 
-        disposables += view.onLayoutChanged(OnLayoutChange(viewModel, childViews, burnInParams))
+        disposables +=
+            view.onLayoutChanged(
+                OnLayoutChange(
+                    viewModel,
+                    blueprintViewModel,
+                    clockViewModel,
+                    childViews,
+                    burnInParams
+                )
+            )
 
         // Views will be added or removed after the call to bind(). This is needed to avoid many
         // calls to findViewById
@@ -404,9 +419,13 @@
 
     private class OnLayoutChange(
         private val viewModel: KeyguardRootViewModel,
+        private val blueprintViewModel: KeyguardBlueprintViewModel,
+        private val clockViewModel: KeyguardClockViewModel,
         private val childViews: Map<Int, View>,
         private val burnInParams: MutableStateFlow<BurnInParameters>,
     ) : OnLayoutChangeListener {
+        var prevTransition: TransitionData? = null
+
         override fun onLayoutChange(
             view: View,
             left: Int,
@@ -418,11 +437,21 @@
             oldRight: Int,
             oldBottom: Int
         ) {
+            // After layout, ensure the notifications are positioned correctly
             childViews[nsslPlaceholderId]?.let { notificationListPlaceholder ->
-                // After layout, ensure the notifications are positioned correctly
+                // Do not update a second time while a blueprint transition is running
+                val transition = blueprintViewModel.currentTransition.value
+                val shouldAnimate = transition != null && transition.config.type.animateNotifChanges
+                if (prevTransition == transition && shouldAnimate) {
+                    if (DEBUG) Log.w(TAG, "Skipping; layout during transition")
+                    return
+                }
+
+                prevTransition = transition
                 viewModel.onNotificationContainerBoundsChanged(
                     notificationListPlaceholder.top.toFloat(),
                     notificationListPlaceholder.bottom.toFloat(),
+                    animate = shouldAnimate
                 )
             }
 
@@ -585,4 +614,6 @@
 
     private const val ID = "occluding_app_device_entry_unlock_msg"
     private const val AOD_ICONS_APPEAR_DURATION: Long = 200
+    private const val TAG = "KeyguardRootViewBinder"
+    private const val DEBUG = false
 }
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 fb1853f..777c873 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,7 +68,9 @@
 import com.android.systemui.keyguard.ui.binder.KeyguardRootViewBinder
 import com.android.systemui.keyguard.ui.view.KeyguardRootView
 import com.android.systemui.keyguard.ui.view.layout.sections.DefaultShortcutsSection
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewClockViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardPreviewSmartspaceViewModel
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModel
@@ -134,6 +136,7 @@
     private val vibratorHelper: VibratorHelper,
     private val indicationController: KeyguardIndicationController,
     private val keyguardRootViewModel: KeyguardRootViewModel,
+    private val keyguardBlueprintViewModel: KeyguardBlueprintViewModel,
     @Assisted bundle: Bundle,
     private val occludingAppDeviceEntryMessageViewModel: OccludingAppDeviceEntryMessageViewModel,
     private val chipbarCoordinator: ChipbarCoordinator,
@@ -143,6 +146,7 @@
     private val communalTutorialViewModel: CommunalTutorialIndicatorViewModel,
     private val defaultShortcutsSection: DefaultShortcutsSection,
     private val keyguardClockInteractor: KeyguardClockInteractor,
+    private val keyguardClockViewModel: KeyguardClockViewModel,
 ) {
     val hostToken: IBinder? = bundle.getBinder(KEY_HOST_TOKEN)
     private val width: Int = bundle.getInt(KEY_VIEW_WIDTH)
@@ -379,12 +383,14 @@
                 KeyguardRootViewBinder.bind(
                     keyguardRootView,
                     keyguardRootViewModel,
+                    keyguardBlueprintViewModel,
                     configuration,
                     occludingAppDeviceEntryMessageViewModel,
                     chipbarCoordinator,
                     screenOffAnimationController,
                     shadeInteractor,
                     keyguardClockInteractor,
+                    keyguardClockViewModel,
                     null, // jank monitor not required for preview mode
                     null, // device entry haptics not required preview mode
                     null, // device entry haptics not required for preview mode
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListener.kt
index 962cdf1..c026656 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListener.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.ui.view.layout
 
 import androidx.core.text.isDigitsOnly
+import com.android.systemui.CoreStartable
 import com.android.systemui.keyguard.data.repository.KeyguardBlueprintRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
 import com.android.systemui.statusbar.commandline.Command
@@ -31,10 +32,10 @@
     private val commandRegistry: CommandRegistry,
     private val keyguardBlueprintRepository: KeyguardBlueprintRepository,
     private val keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
-) {
+) : CoreStartable {
     private val layoutCommand = KeyguardLayoutManagerCommand()
 
-    fun start() {
+    override fun start() {
         commandRegistry.registerCommand(COMMAND) { layoutCommand }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
index 04ac7bf..2dc9301 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/KeyguardBlueprintModule.kt
@@ -17,9 +17,14 @@
 
 package com.android.systemui.keyguard.ui.view.layout.blueprints
 
+import com.android.systemui.CoreStartable
+import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardBlueprint
+import com.android.systemui.keyguard.ui.view.layout.KeyguardBlueprintCommandListener
 import dagger.Binds
 import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
 import dagger.multibindings.IntoSet
 
 @Module
@@ -41,4 +46,18 @@
     abstract fun bindShortcutsBesideUdfpsLockscreenBlueprint(
         shortcutsBesideUdfpsLockscreenBlueprint: ShortcutsBesideUdfpsKeyguardBlueprint
     ): KeyguardBlueprint
+
+    @Binds
+    @IntoMap
+    @ClassKey(KeyguardBlueprintInteractor::class)
+    abstract fun bindsKeyguardBlueprintInteractor(
+        keyguardBlueprintInteractor: KeyguardBlueprintInteractor
+    ): CoreStartable
+
+    @Binds
+    @IntoMap
+    @ClassKey(KeyguardBlueprintCommandListener::class)
+    abstract fun bindsKeyguardBlueprintCommandListener(
+        keyguardBlueprintCommandListener: KeyguardBlueprintCommandListener
+    ): CoreStartable
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
index c69d868..39f1ebe 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/blueprints/transitions/IntraBlueprintTransition.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.ui.view.layout.blueprints.transitions
 
 import android.transition.TransitionSet
+import com.android.systemui.keyguard.shared.model.KeyguardSection
 import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition
 import com.android.systemui.keyguard.ui.view.layout.sections.transitions.DefaultClockSteppingTransition
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
@@ -30,22 +31,23 @@
 
     enum class Type(
         val priority: Int,
+        val animateNotifChanges: Boolean,
     ) {
-        ClockSize(100),
-        ClockCenter(99),
-        DefaultClockStepping(98),
-        AodNotifIconsTransition(97),
-        SmartspaceVisibility(2),
-        DefaultTransition(1),
+        ClockSize(100, true),
+        ClockCenter(99, false),
+        DefaultClockStepping(98, false),
+        SmartspaceVisibility(2, true),
+        DefaultTransition(1, false),
         // When transition between blueprint, we don't need any duration or interpolator but we need
         // all elements go to correct state
-        NoTransition(0),
+        NoTransition(0, false),
     }
 
     data class Config(
         val type: Type,
         val checkPriority: Boolean = true,
         val terminatePrevious: Boolean = true,
+        val rebuildSections: List<KeyguardSection> = listOf(),
     ) {
         companion object {
             val DEFAULT = Config(Type.NoTransition)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
index 2832e9d..d77b548 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/AodNotificationIconsSection.kt
@@ -19,6 +19,8 @@
 
 import android.content.Context
 import android.view.View
+import android.view.View.GONE
+import android.view.View.VISIBLE
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
@@ -29,6 +31,7 @@
 import com.android.systemui.common.ui.ConfigurationState
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.shared.model.KeyguardSection
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerViewBinder
@@ -38,6 +41,7 @@
 import com.android.systemui.statusbar.phone.NotificationIconAreaController
 import com.android.systemui.statusbar.phone.NotificationIconContainer
 import com.android.systemui.statusbar.ui.SystemBarUtilsState
+import com.android.systemui.util.ui.value
 import javax.inject.Inject
 import kotlinx.coroutines.DisposableHandle
 
@@ -51,6 +55,7 @@
     private val nicAodIconViewStore: AlwaysOnDisplayNotificationIconViewStore,
     private val notificationIconAreaController: NotificationIconAreaController,
     private val systemBarUtilsState: SystemBarUtilsState,
+    private val rootViewModel: KeyguardRootViewModel,
 ) : KeyguardSection() {
 
     private var nicBindingDisposable: DisposableHandle? = null
@@ -101,20 +106,14 @@
         if (!MigrateClocksToBlueprint.isEnabled) {
             return
         }
+
         val bottomMargin =
             context.resources.getDimensionPixelSize(R.dimen.keyguard_status_view_bottom_margin)
-
-        val useSplitShade = context.resources.getBoolean(R.bool.config_use_split_notification_shade)
-
-        val topAlignment =
-            if (useSplitShade) {
-                TOP
-            } else {
-                BOTTOM
-            }
+        val isVisible = rootViewModel.isNotifIconContainerVisible.value
         constraintSet.apply {
             connect(nicId, TOP, R.id.smart_space_barrier_bottom, BOTTOM, bottomMargin)
             setGoneMargin(nicId, BOTTOM, bottomMargin)
+            setVisibility(nicId, if (isVisible.value) VISIBLE else GONE)
 
             connect(
                 nicId,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index b367715..34a1da5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -32,6 +32,7 @@
 import androidx.constraintlayout.widget.ConstraintSet.VISIBLE
 import androidx.constraintlayout.widget.ConstraintSet.WRAP_CONTENT
 import com.android.systemui.customization.R as custR
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
@@ -57,6 +58,7 @@
     alpha: Float,
 ) = views.forEach { view -> this.setAlpha(view.id, alpha) }
 
+@SysUISingleton
 class ClockSection
 @Inject
 constructor(
@@ -72,6 +74,7 @@
         if (!MigrateClocksToBlueprint.isEnabled) {
             return
         }
+
         KeyguardClockViewBinder.bind(
             this,
             constraintLayout,
@@ -86,6 +89,7 @@
         if (!MigrateClocksToBlueprint.isEnabled) {
             return
         }
+
         keyguardClockViewModel.currentClock.value?.let { clock ->
             constraintSet.applyDeltaFrom(buildConstraints(clock, constraintSet))
         }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index 0b8376a..e01f0a1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
@@ -147,8 +147,9 @@
                 deviceEntryIconViewModel.get().udfpsLocation.value?.let { udfpsLocation ->
                     Log.d(
                         "DeviceEntrySection",
-                        "udfpsLocation=$udfpsLocation" +
-                            " unusedAuthController=${authController.udfpsLocation}"
+                        "udfpsLocation=$udfpsLocation, " +
+                            "scaledLocation=(${udfpsLocation.centerX},${udfpsLocation.centerY}), " +
+                            "unusedAuthController=${authController.udfpsLocation}"
                     )
                     centerIcon(
                         Point(udfpsLocation.centerX.toInt(), udfpsLocation.centerY.toInt()),
@@ -219,5 +220,37 @@
                 sensorRect.left
             )
         }
+
+        // This is only intended to be here until the KeyguardBottomAreaRefactor flag is enabled
+        // Without this logic, the lock icon location changes but the KeyguardBottomAreaView is not
+        // updated and visible ui layout jank occurs. This is due to AmbientIndicationContainer
+        // being in NPVC and laying out prior to the KeyguardRootView.
+        // Remove when both DeviceEntryUdfpsRefactor and KeyguardBottomAreaRefactor are enabled.
+        if (DeviceEntryUdfpsRefactor.isEnabled && !KeyguardBottomAreaRefactor.isEnabled) {
+            with(notificationPanelView) {
+                val isUdfpsSupported = deviceEntryIconViewModel.get().isUdfpsSupported.value
+                val bottomAreaViewRight = findViewById<View>(R.id.keyguard_bottom_area)?.right ?: 0
+                findViewById<View>(R.id.ambient_indication_container)?.let {
+                    val (ambientLeft, ambientTop) = it.locationOnScreen
+                    if (isUdfpsSupported) {
+                        // make top of ambient indication view the bottom of the lock icon
+                        it.layout(
+                            ambientLeft,
+                            sensorRect.bottom,
+                            bottomAreaViewRight - ambientLeft,
+                            ambientTop + it.measuredHeight
+                        )
+                    } else {
+                        // make bottom of ambient indication view the top of the lock icon
+                        it.layout(
+                            ambientLeft,
+                            sensorRect.top - it.measuredHeight,
+                            bottomAreaViewRight - ambientLeft,
+                            sensorRect.top
+                        )
+                    }
+                }
+            }
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
index 487c2e9..0435531 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
@@ -22,6 +22,7 @@
 import androidx.constraintlayout.widget.Barrier
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
@@ -36,6 +37,7 @@
 import dagger.Lazy
 import javax.inject.Inject
 
+@SysUISingleton
 open class SmartspaceSection
 @Inject
 constructor(
@@ -54,6 +56,14 @@
     private var smartspaceVisibilityListener: OnGlobalLayoutListener? = null
     private var pastVisibility: Int = -1
 
+    override fun onRebuildBegin() {
+        smartspaceController.suppressDisconnects = true
+    }
+
+    override fun onRebuildEnd() {
+        smartspaceController.suppressDisconnects = false
+    }
+
     override fun addViews(constraintLayout: ConstraintLayout) {
         if (!MigrateClocksToBlueprint.isEnabled) return
         if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
index 7c745bc..f17dbd2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
@@ -369,6 +369,21 @@
             addTarget(R.id.status_view_media_container)
         }
 
+        override fun mutateBounds(
+            view: View,
+            fromIsVis: Boolean,
+            toIsVis: Boolean,
+            fromBounds: Rect,
+            toBounds: Rect,
+            fromSSBounds: Rect?,
+            toSSBounds: Rect?
+        ) {
+            // If view is changing visibility, hold it in place
+            if (fromIsVis == toIsVis) return
+            if (DEBUG) Log.i(TAG, "Holding position of ${view.id}")
+            toBounds.set(fromBounds)
+        }
+
         companion object {
             const val STATUS_AREA_MOVE_UP_MILLIS = 967L
             const val STATUS_AREA_MOVE_DOWN_MILLIS = 467L
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
index b1f1898..7ac03bf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModel.kt
@@ -17,15 +17,119 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.os.Handler
+import android.transition.Transition
+import android.transition.TransitionManager
+import android.util.Log
+import androidx.constraintlayout.widget.ConstraintLayout
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
+import com.android.systemui.keyguard.ui.view.layout.blueprints.transitions.IntraBlueprintTransition.Config
 import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+data class TransitionData(
+    val config: Config,
+    val start: Long = System.currentTimeMillis(),
+)
 
 class KeyguardBlueprintViewModel
 @Inject
 constructor(
+    @Main private val handler: Handler,
     keyguardBlueprintInteractor: KeyguardBlueprintInteractor,
 ) {
     val blueprint = keyguardBlueprintInteractor.blueprint
     val blueprintId = keyguardBlueprintInteractor.blueprintId
     val refreshTransition = keyguardBlueprintInteractor.refreshTransition
+
+    private val _currentTransition = MutableStateFlow<TransitionData?>(null)
+    val currentTransition = _currentTransition.asStateFlow()
+
+    private val runningTransitions = mutableSetOf<Transition>()
+    private val transitionListener =
+        object : Transition.TransitionListener {
+            override fun onTransitionCancel(transition: Transition) {
+                if (DEBUG) Log.e(TAG, "onTransitionCancel: ${transition::class.simpleName}")
+                updateTransitions(null) { remove(transition) }
+            }
+
+            override fun onTransitionEnd(transition: Transition) {
+                if (DEBUG) Log.e(TAG, "onTransitionEnd: ${transition::class.simpleName}")
+                updateTransitions(null) { remove(transition) }
+            }
+
+            override fun onTransitionPause(transition: Transition) {
+                if (DEBUG) Log.i(TAG, "onTransitionPause: ${transition::class.simpleName}")
+                updateTransitions(null) { remove(transition) }
+            }
+
+            override fun onTransitionResume(transition: Transition) {
+                if (DEBUG) Log.i(TAG, "onTransitionResume: ${transition::class.simpleName}")
+                updateTransitions(null) { add(transition) }
+            }
+
+            override fun onTransitionStart(transition: Transition) {
+                if (DEBUG) Log.i(TAG, "onTransitionStart: ${transition::class.simpleName}")
+                updateTransitions(null) { add(transition) }
+            }
+        }
+
+    fun updateTransitions(data: TransitionData?, mutate: MutableSet<Transition>.() -> Unit) {
+        runningTransitions.mutate()
+
+        if (runningTransitions.size <= 0) _currentTransition.value = null
+        else if (data != null) _currentTransition.value = data
+    }
+
+    fun runTransition(
+        constraintLayout: ConstraintLayout,
+        transition: Transition,
+        config: Config,
+        apply: () -> Unit,
+    ) {
+        val currentPriority = currentTransition.value?.let { it.config.type.priority } ?: -1
+        if (config.checkPriority && config.type.priority < currentPriority) {
+            if (DEBUG) {
+                Log.w(
+                    TAG,
+                    "runTransition: skipping ${transition::class.simpleName}: " +
+                        "currentPriority=$currentPriority; config=$config"
+                )
+            }
+            apply()
+            return
+        }
+
+        if (DEBUG) {
+            Log.i(
+                TAG,
+                "runTransition: running ${transition::class.simpleName}: " +
+                    "currentPriority=$currentPriority; config=$config"
+            )
+        }
+
+        // beginDelayedTransition makes a copy, so we temporarially add the uncopied transition to
+        // the running set until the copy is started by the handler.
+        updateTransitions(TransitionData(config)) { add(transition) }
+        transition.addListener(transitionListener)
+
+        handler.post {
+            if (config.terminatePrevious) {
+                TransitionManager.endTransitions(constraintLayout)
+            }
+
+            TransitionManager.beginDelayedTransition(constraintLayout, transition)
+            apply()
+
+            // Delay removal until after copied transition has started
+            handler.post { updateTransitions(null) { remove(transition) } }
+        }
+    }
+
+    companion object {
+        private const val TAG = "KeyguardBlueprintViewModel"
+        private const val DEBUG = true
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
index 198e9f2..940f423 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
@@ -65,7 +65,7 @@
             }
             .stateIn(
                 scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
+                started = SharingStarted.Eagerly,
                 initialValue = ClockSize.LARGE,
             )
 
@@ -74,7 +74,7 @@
             .map { it == ClockSize.LARGE }
             .stateIn(
                 scope = applicationScope,
-                started = SharingStarted.WhileSubscribed(),
+                started = SharingStarted.Eagerly,
                 initialValue = true,
             )
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
index 8409f15..448a71c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardIndicationAreaViewModel.kt
@@ -23,9 +23,12 @@
 import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.BurnInModel
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.res.R
 import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.distinctUntilChanged
@@ -42,6 +45,7 @@
     private val burnInInteractor: BurnInInteractor,
     private val shortcutsCombinedViewModel: KeyguardQuickAffordancesCombinedViewModel,
     configurationInteractor: ConfigurationInteractor,
+    keyguardTransitionInteractor: KeyguardTransitionInteractor,
 ) {
 
     /** Notifies when a new configuration is set */
@@ -69,12 +73,22 @@
                 .distinctUntilChanged()
         }
 
+    @OptIn(ExperimentalCoroutinesApi::class)
     private val burnIn: Flow<BurnInModel> =
-        burnInInteractor
-            .burnIn(
-                xDimenResourceId = R.dimen.burn_in_prevention_offset_x,
-                yDimenResourceId = R.dimen.default_burn_in_prevention_offset,
-            )
+        combine(
+                burnInInteractor.burnIn(
+                    xDimenResourceId = R.dimen.burn_in_prevention_offset_x,
+                    yDimenResourceId = R.dimen.default_burn_in_prevention_offset,
+                ),
+                keyguardTransitionInteractor.transitionValue(KeyguardState.AOD),
+            ) { burnIn, aodTransitionValue ->
+                BurnInModel(
+                    (burnIn.translationX * aodTransitionValue).toInt(),
+                    (burnIn.translationY * aodTransitionValue).toInt(),
+                    burnIn.scale,
+                    burnIn.scaleClockOnly,
+                )
+            }
             .distinctUntilChanged()
 
     /** An observable for the x-offset by which the indication area should be translated. */
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 aaec69f..1ec2a49 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
@@ -58,6 +58,8 @@
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.combineTransform
@@ -67,13 +69,14 @@
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.onStart
+import kotlinx.coroutines.flow.stateIn
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class KeyguardRootViewModel
 @Inject
 constructor(
-    @Application private val scope: CoroutineScope,
+    @Application private val applicationScope: CoroutineScope,
     private val deviceEntryInteractor: DeviceEntryInteractor,
     private val dozeParameters: DozeParameters,
     private val keyguardInteractor: KeyguardInteractor,
@@ -280,7 +283,7 @@
         burnInJob?.cancel()
 
         burnInJob =
-            scope.launch("$TAG#aodBurnInViewModel") {
+            applicationScope.launch("$TAG#aodBurnInViewModel") {
                 aodBurnInViewModel.movement(params).collect { _burnInModel.value = it }
             }
     }
@@ -294,7 +297,7 @@
         }
 
     /** Is the notification icon container visible? */
-    val isNotifIconContainerVisible: Flow<AnimatedValue<Boolean>> =
+    val isNotifIconContainerVisible: StateFlow<AnimatedValue<Boolean>> =
         combine(
                 goneToAodTransitionRunning,
                 keyguardTransitionInteractor.finishedKeyguardState.map {
@@ -336,11 +339,15 @@
                         }
                 }
             }
-            .distinctUntilChanged()
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.WhileSubscribed(),
+                initialValue = AnimatedValue.NotAnimating(false),
+            )
 
-    fun onNotificationContainerBoundsChanged(top: Float, bottom: Float) {
+    fun onNotificationContainerBoundsChanged(top: Float, bottom: Float, animate: Boolean = false) {
         keyguardInteractor.setNotificationContainerBounds(
-            NotificationContainerBounds(top = top, bottom = bottom)
+            NotificationContainerBounds(top = top, bottom = bottom, isAnimated = animate)
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/MediaDomainModule.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/MediaDomainModule.kt
index e0c5419..9c29bab 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/MediaDomainModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/MediaDomainModule.kt
@@ -43,6 +43,7 @@
     @IntoMap
     @ClassKey(MediaDataProcessor::class)
     fun bindMediaDataProcessor(interactor: MediaDataProcessor): CoreStartable
+
     companion object {
 
         @Provides
@@ -52,7 +53,7 @@
             newProvider: Provider<MediaCarouselInteractor>,
             mediaFlags: MediaFlags,
         ): MediaDataManager {
-            return if (mediaFlags.isMediaControlsRefactorEnabled()) {
+            return if (mediaFlags.isSceneContainerEnabled()) {
                 newProvider.get()
             } else {
                 legacyProvider.get()
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 eed7752..8e985e1 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
@@ -269,7 +269,7 @@
         }
 
     override fun start() {
-        if (!mediaFlags.isMediaControlsRefactorEnabled()) {
+        if (!mediaFlags.isSceneContainerEnabled()) {
             return
         }
 
@@ -746,8 +746,7 @@
             notif.extras.getParcelable(
                 Notification.EXTRA_BUILDER_APPLICATION_INFO,
                 ApplicationInfo::class.java
-            )
-                ?: getAppInfoFromPackage(sbn.packageName)
+            ) ?: getAppInfoFromPackage(sbn.packageName)
 
         // App name
         val appName = getAppName(sbn, appInfo)
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 486d4d4..aa93df7 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
@@ -23,6 +23,7 @@
 import android.media.MediaRouter2Manager
 import android.media.RoutingSessionInfo
 import android.media.session.MediaController
+import android.media.session.MediaController.PlaybackInfo
 import android.text.TextUtils
 import android.util.Log
 import androidx.annotation.AnyThread
@@ -74,6 +75,11 @@
     private val listeners: MutableSet<Listener> = mutableSetOf()
     private val entries: MutableMap<String, Entry> = mutableMapOf()
 
+    companion object {
+        private val EMPTY_AND_DISABLED_MEDIA_DEVICE_DATA =
+            MediaDeviceData(enabled = false, icon = null, name = null, showBroadcastButton = false)
+    }
+
     /** Add a listener for changes to the media route (ie. device). */
     fun addListener(listener: Listener) = listeners.add(listener)
 
@@ -333,28 +339,32 @@
         @WorkerThread
         private fun updateCurrent() {
             if (isLeAudioBroadcastEnabled()) {
-                if (enableLeAudioSharing()) {
-                    current =
-                        MediaDeviceData(
-                            enabled = false,
-                            icon =
-                                context.getDrawable(
-                                    com.android.settingslib.R.drawable.ic_bt_le_audio_sharing
-                                ),
-                            name = context.getString(R.string.audio_sharing_description),
-                            intent = null,
-                            showBroadcastButton = false
-                        )
+                current = getLeAudioBroadcastDeviceData()
+            } else if (Flags.usePlaybackInfoForRoutingControls()) {
+                val activeDevice: MediaDeviceData?
+
+                // LocalMediaManager provides the connected device based on PlaybackInfo.
+                // TODO (b/342197065): Simplify nullability once we make currentConnectedDevice
+                //  non-null.
+                val connectedDevice = localMediaManager.currentConnectedDevice?.toMediaDeviceData()
+
+                if (controller?.playbackInfo?.playbackType == PlaybackInfo.PLAYBACK_TYPE_REMOTE) {
+                    val routingSession =
+                        mr2manager.get().getRoutingSessionForMediaController(controller)
+
+                    activeDevice =
+                        routingSession?.let {
+                            // For a remote session, always use the current device from
+                            // LocalMediaManager. Override with routing session name if available to
+                            // show dynamic group name.
+                            connectedDevice?.copy(name = it.name ?: connectedDevice.name)
+                        }
                 } else {
-                    current =
-                        MediaDeviceData(
-                            /* enabled */ true,
-                            /* icon */ context.getDrawable(R.drawable.settings_input_antenna),
-                            /* name */ broadcastDescription,
-                            /* intent */ null,
-                            /* showBroadcastButton */ showBroadcastButton = true
-                        )
+                    // Prefer SASS if available when playback is local.
+                    activeDevice = getSassDevice() ?: connectedDevice
                 }
+
+                current = activeDevice ?: EMPTY_AND_DISABLED_MEDIA_DEVICE_DATA
             } else {
                 val aboutToConnect = aboutToConnectDeviceOverride
                 if (
@@ -389,6 +399,43 @@
             }
         }
 
+        private fun getSassDevice(): MediaDeviceData? {
+            val sassDevice = aboutToConnectDeviceOverride ?: return null
+            return sassDevice.fullMediaDevice?.toMediaDeviceData()
+                ?: sassDevice.backupMediaDeviceData
+        }
+
+        private fun MediaDevice.toMediaDeviceData() =
+            MediaDeviceData(
+                enabled = true,
+                icon = iconWithoutBackground,
+                name = name,
+                id = id,
+                showBroadcastButton = false
+            )
+
+        private fun getLeAudioBroadcastDeviceData(): MediaDeviceData {
+            return if (enableLeAudioSharing()) {
+                MediaDeviceData(
+                    enabled = false,
+                    icon =
+                        context.getDrawable(
+                            com.android.settingslib.R.drawable.ic_bt_le_audio_sharing
+                        ),
+                    name = context.getString(R.string.audio_sharing_description),
+                    intent = null,
+                    showBroadcastButton = false
+                )
+            } else {
+                MediaDeviceData(
+                    enabled = true,
+                    icon = context.getDrawable(R.drawable.settings_input_antenna),
+                    name = broadcastDescription,
+                    intent = null,
+                    showBroadcastButton = true
+                )
+            }
+        }
         /** Return a display name for the current device / route, or null if not possible */
         private fun getDeviceName(
             device: MediaDevice?,
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
index 9e62300..b4bd4fd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt
@@ -36,8 +36,8 @@
 import com.android.systemui.media.controls.domain.pipeline.MediaTimeoutListener
 import com.android.systemui.media.controls.domain.resume.MediaResumeListener
 import com.android.systemui.media.controls.shared.model.MediaCommonModel
-import com.android.systemui.media.controls.util.MediaControlsRefactorFlag
 import com.android.systemui.media.controls.util.MediaFlags
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import java.io.PrintWriter
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -127,7 +127,7 @@
     val currentMedia: StateFlow<List<MediaCommonModel>> = mediaFilterRepository.currentMedia
 
     override fun start() {
-        if (!mediaFlags.isMediaControlsRefactorEnabled()) {
+        if (!mediaFlags.isSceneContainerEnabled()) {
             return
         }
 
@@ -256,8 +256,6 @@
     companion object {
         val unsupported: Nothing
             get() =
-                error(
-                    "Code path not supported when ${MediaControlsRefactorFlag.FLAG_NAME} is enabled"
-                )
+                error("Code path not supported when ${SceneContainerFlag.DESCRIPTION} is enabled")
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
index fed93f0..72fb218 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt
@@ -157,7 +157,6 @@
             viewController,
             backgroundDispatcher,
             mainDispatcher,
-            mediaFlags,
             isSongUpdated
         )
 
@@ -414,7 +413,6 @@
         viewController: MediaViewController,
         backgroundDispatcher: CoroutineDispatcher,
         mainDispatcher: CoroutineDispatcher,
-        mediaFlags: MediaFlags,
         updateBackground: Boolean,
     ) {
         val traceCookie = viewHolder.hashCode()
@@ -424,13 +422,8 @@
             viewController.isArtworkBound = false
         }
         // Capture width & height from views in foreground for artwork scaling in background
-        var width = viewHolder.albumView.measuredWidth
-        var height = viewHolder.albumView.measuredHeight
-        if (mediaFlags.isSceneContainerEnabled() && (width <= 0 || height <= 0)) {
-            // TODO(b/312714128): ensure we have a valid size before setting background
-            width = viewController.widthInSceneContainerPx
-            height = viewController.heightInSceneContainerPx
-        }
+        val width = viewController.widthInSceneContainerPx
+        val height = viewController.heightInSceneContainerPx
         withContext(backgroundDispatcher) {
             val artwork =
                 if (viewModel.shouldAddGradient) {
@@ -449,6 +442,11 @@
                 val colorSchemeChanged =
                     viewController.colorSchemeTransition.updateColorScheme(viewModel.colorScheme)
                 val albumView = viewHolder.albumView
+
+                // Set up width of album view constraint.
+                viewController.expandedLayout.getConstraint(albumView.id).layout.mWidth = width
+                viewController.collapsedLayout.getConstraint(albumView.id).layout.mWidth = width
+
                 albumView.setPadding(0, 0, 0, 0)
                 if (
                     updateBackground ||
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 19e3e07..edead51 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
@@ -42,6 +42,7 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.systemui.Dumpable
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
@@ -123,6 +124,7 @@
 class MediaCarouselController
 @Inject
 constructor(
+    @Application applicationScope: CoroutineScope,
     private val context: Context,
     private val mediaControlPanelFactory: Provider<MediaControlPanel>,
     private val visualStabilityProvider: VisualStabilityProvider,
@@ -217,7 +219,7 @@
     private val animationScaleObserver: ContentObserver =
         object : ContentObserver(null) {
             override fun onChange(selfChange: Boolean) {
-                if (!mediaFlags.isMediaControlsRefactorEnabled()) {
+                if (!mediaFlags.isSceneContainerEnabled()) {
                     MediaPlayerData.players().forEach { it.updateAnimatorDurationScale() }
                 } else {
                     controllerByViewModel.values.forEach { it.updateAnimatorDurationScale() }
@@ -347,7 +349,7 @@
         inflateSettingsButton()
         mediaContent = mediaCarousel.requireViewById(R.id.media_carousel)
         configurationController.addCallback(configListener)
-        if (!mediaFlags.isMediaControlsRefactorEnabled()) {
+        if (!mediaFlags.isSceneContainerEnabled()) {
             setUpListeners()
         } else {
             val visualStabilityCallback = OnReorderingAllowedListener {
@@ -387,15 +389,15 @@
             repeatOnLifecycle(Lifecycle.State.STARTED) {
                 listenForAnyStateToGoneKeyguardTransition(this)
                 listenForAnyStateToLockscreenTransition(this)
-                listenForLockscreenSettingChanges(this)
 
-                if (!mediaFlags.isMediaControlsRefactorEnabled()) return@repeatOnLifecycle
+                if (!mediaFlags.isSceneContainerEnabled()) return@repeatOnLifecycle
                 listenForMediaItemsChanges(this)
             }
         }
+        listenForLockscreenSettingChanges(applicationScope)
 
         // Notifies all active players about animation scale changes.
-        globalSettings.registerContentObserver(
+        globalSettings.registerContentObserverSync(
             Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
             animationScaleObserver
         )
@@ -882,8 +884,7 @@
                     val previousVisibleIndex =
                         MediaPlayerData.playerKeys().indexOfFirst { key -> it == key }
                     mediaCarouselScrollHandler.scrollToPlayer(previousVisibleIndex, mediaIndex)
-                }
-                    ?: mediaCarouselScrollHandler.scrollToPlayer(destIndex = mediaIndex)
+                } ?: mediaCarouselScrollHandler.scrollToPlayer(destIndex = mediaIndex)
             }
         } else if (isRtl && mediaContent.childCount > 0) {
             // In RTL, Scroll to the first player as it is the rightmost player in media carousel.
@@ -1092,7 +1093,7 @@
     }
 
     private fun updatePlayers(recreateMedia: Boolean) {
-        if (mediaFlags.isMediaControlsRefactorEnabled()) {
+        if (mediaFlags.isSceneContainerEnabled()) {
             updateMediaPlayers(recreateMedia)
             return
         }
@@ -1192,7 +1193,7 @@
             currentStartLocation = startLocation
             currentEndLocation = endLocation
             currentTransitionProgress = progress
-            if (!mediaFlags.isMediaControlsRefactorEnabled()) {
+            if (!mediaFlags.isSceneContainerEnabled()) {
                 for (mediaPlayer in MediaPlayerData.players()) {
                     updateViewControllerToState(mediaPlayer.mediaViewController, immediately)
                 }
@@ -1254,7 +1255,7 @@
 
     /** Update listening to seekbar. */
     private fun updateSeekbarListening(visibleToUser: Boolean) {
-        if (!mediaFlags.isMediaControlsRefactorEnabled()) {
+        if (!mediaFlags.isSceneContainerEnabled()) {
             for (player in MediaPlayerData.players()) {
                 player.setListening(visibleToUser && currentlyExpanded)
             }
@@ -1269,7 +1270,7 @@
     private fun updateCarouselDimensions() {
         var width = 0
         var height = 0
-        if (!mediaFlags.isMediaControlsRefactorEnabled()) {
+        if (!mediaFlags.isSceneContainerEnabled()) {
             for (mediaPlayer in MediaPlayerData.players()) {
                 val controller = mediaPlayer.mediaViewController
                 // When transitioning the view to gone, the view gets smaller, but the translation
@@ -1361,7 +1362,7 @@
                         !mediaManager.hasActiveMediaOrRecommendation() &&
                         desiredHostState.showsOnlyActiveMedia
 
-                if (!mediaFlags.isMediaControlsRefactorEnabled()) {
+                if (!mediaFlags.isSceneContainerEnabled()) {
                     for (mediaPlayer in MediaPlayerData.players()) {
                         if (animate) {
                             mediaPlayer.mediaViewController.animatePendingStateChange(
@@ -1401,7 +1402,7 @@
         }
 
     fun closeGuts(immediate: Boolean = true) {
-        if (!mediaFlags.isMediaControlsRefactorEnabled()) {
+        if (!mediaFlags.isSceneContainerEnabled()) {
             MediaPlayerData.players().forEach { it.closeGuts(immediate) }
         } else {
             controllerByViewModel.values.forEach { it.closeGuts(immediate) }
@@ -1544,7 +1545,7 @@
 
     @VisibleForTesting
     fun onSwipeToDismiss() {
-        if (mediaFlags.isMediaControlsRefactorEnabled()) {
+        if (mediaFlags.isSceneContainerEnabled()) {
             mediaCarouselViewModel.onSwipeToDismiss()
             return
         }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index a4f3e21..601d563 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -42,6 +42,7 @@
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dreams.DreamOverlayStateController
 import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
 import com.android.systemui.media.controls.ui.view.MediaHost
 import com.android.systemui.media.controls.util.MediaFlags
@@ -61,6 +62,11 @@
 import com.android.systemui.util.settings.SecureSettings
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.launch
 
 private val TAG: String = MediaHierarchyManager::class.java.simpleName
@@ -89,6 +95,7 @@
  * This manager is responsible for placement of the unique media view between the different hosts
  * and animate the positions of the views to achieve seamless transitions.
  */
+@OptIn(ExperimentalCoroutinesApi::class)
 @SysUISingleton
 class MediaHierarchyManager
 @Inject
@@ -101,6 +108,7 @@
     private val mediaManager: MediaDataManager,
     private val keyguardViewController: KeyguardViewController,
     private val dreamOverlayStateController: DreamOverlayStateController,
+    private val keyguardInteractor: KeyguardInteractor,
     communalTransitionViewModel: CommunalTransitionViewModel,
     configurationController: ConfigurationController,
     wakefulnessLifecycle: WakefulnessLifecycle,
@@ -159,6 +167,7 @@
     private var targetBounds: Rect = Rect()
     private val mediaFrame
         get() = mediaCarouselController.mediaFrame
+
     private var statusbarState: Int = statusBarStateController.state
     private var animator =
         ValueAnimator.ofFloat(0.0f, 1.0f).apply {
@@ -236,6 +245,15 @@
 
     private var inSplitShade = false
 
+    /**
+     * Whether we are transitioning to the hub or from the hub to the shade. If so, use fade as the
+     * transformation type and skip calculating state with the bounds and the transition progress.
+     */
+    private val isHubTransition
+        get() =
+            desiredLocation == LOCATION_COMMUNAL_HUB ||
+                (previousLocation == LOCATION_COMMUNAL_HUB && desiredLocation == LOCATION_QS)
+
     /** Is there any active media or recommendation in the carousel? */
     private var hasActiveMediaOrRecommendation: Boolean = false
         get() = mediaManager.hasActiveMediaOrRecommendation()
@@ -413,6 +431,12 @@
     /** Is the communal UI showing */
     private var isCommunalShowing: Boolean = false
 
+    /** Is the communal UI showing and not dreaming */
+    private var onCommunalNotDreaming: Boolean = false
+
+    /** Is the communal UI showing, dreaming and shade expanding */
+    private var onCommunalDreamingAndShadeExpanding: Boolean = false
+
     /**
      * The current cross fade progress. 0.5f means it's just switching between the start and the end
      * location and the content is fully faded, while 0.75f means that we're halfway faded in again
@@ -577,7 +601,7 @@
                     }
                 }
             }
-        secureSettings.registerContentObserverForUser(
+        secureSettings.registerContentObserverForUserSync(
             Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN,
             settingsObserver,
             UserHandle.USER_ALL
@@ -585,11 +609,26 @@
 
         // Listen to the communal UI state. Make sure that communal UI is showing and hub itself is
         // available, ie. not disabled and able to be shown.
+        // When dreaming, qs expansion is immediately set to 1f, so we listen to shade expansion to
+        // calculate the new location.
         coroutineScope.launch {
-            communalTransitionViewModel.isUmoOnCommunal.collect { value ->
-                isCommunalShowing = value
-                updateDesiredLocation(forceNoAnimation = true)
-            }
+            combine(
+                    communalTransitionViewModel.isUmoOnCommunal,
+                    keyguardInteractor.isDreaming,
+                    // keep on communal before the shade is expanded enough to show the elements in
+                    // QS
+                    shadeInteractor.shadeExpansion
+                        .mapLatest { it < EXPANSION_THRESHOLD }
+                        .distinctUntilChanged(),
+                    ::Triple
+                )
+                .collectLatest { (communalShowing, isDreaming, isShadeExpanding) ->
+                    isCommunalShowing = communalShowing
+                    onCommunalDreamingAndShadeExpanding =
+                        communalShowing && isDreaming && isShadeExpanding
+                    onCommunalNotDreaming = communalShowing && !isDreaming
+                    updateDesiredLocation(forceNoAnimation = true)
+                }
         }
     }
 
@@ -805,6 +844,9 @@
         if (skipQqsOnExpansion) {
             return false
         }
+        if (isHubTransition) {
+            return false
+        }
         // This is an invalid transition, and can happen when using the camera gesture from the
         // lock screen. Disallow.
         if (
@@ -947,6 +989,9 @@
     @VisibleForTesting
     @TransformationType
     fun calculateTransformationType(): Int {
+        if (isHubTransition) {
+            return TRANSFORMATION_TYPE_FADE
+        }
         if (isTransitioningToFullShade) {
             if (inSplitShade && areGuidedTransitionHostsVisible()) {
                 return TRANSFORMATION_TYPE_TRANSITION
@@ -977,7 +1022,7 @@
      *   otherwise
      */
     private fun getTransformationProgress(): Float {
-        if (skipQqsOnExpansion) {
+        if (skipQqsOnExpansion || isHubTransition) {
             return -1.0f
         }
         val progress = getQSTransformationProgress()
@@ -1147,15 +1192,18 @@
         }
         val onLockscreen =
             (!bypassController.bypassEnabled && (statusbarState == StatusBarState.KEYGUARD))
+
+        // UMO should show on hub unless the qs is expanding when not dreaming, or shade is
+        // expanding when dreaming
+        val onCommunal =
+            (onCommunalNotDreaming && qsExpansion == 0.0f) || onCommunalDreamingAndShadeExpanding
         val location =
             when {
                 mediaFlags.isSceneContainerEnabled() -> desiredLocation
                 dreamOverlayActive && dreamMediaComplicationActive -> LOCATION_DREAM_OVERLAY
-
-                // UMO should show in communal unless the shade is expanding or visible.
-                isCommunalShowing && qsExpansion == 0.0f -> LOCATION_COMMUNAL_HUB
+                onCommunal -> LOCATION_COMMUNAL_HUB
                 (qsExpansion > 0.0f || inSplitShade) && !onLockscreen -> LOCATION_QS
-                qsExpansion > 0.4f && onLockscreen -> LOCATION_QS
+                qsExpansion > EXPANSION_THRESHOLD && onLockscreen -> LOCATION_QS
                 onLockscreen && isSplitShadeExpanding() -> LOCATION_QS
                 onLockscreen && isTransformingToFullShadeAndInQQS() -> LOCATION_QQS
 
@@ -1190,6 +1238,9 @@
             // reattach it without an animation
             return LOCATION_LOCKSCREEN
         }
+        // When communal showing while dreaming, skipQqsOnExpansion is also true but we want to
+        // return the calculated location, so it won't disappear as soon as shade is pulled down.
+        if (isCommunalShowing) return location
         if (skipQqsOnExpansion) {
             // When doing an immediate expand or collapse, we want to keep it in QS.
             return LOCATION_QS
@@ -1288,6 +1339,9 @@
          * transitioning
          */
         const val TRANSFORMATION_TYPE_FADE = 1
+
+        /** Expansion amount value at which elements start to become visible in the QS panel. */
+        const val EXPANSION_THRESHOLD = 0.4f
     }
 }
 
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 3837708..9d07232 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
@@ -203,7 +203,7 @@
     private val scrubbingChangeListener =
         object : SeekBarViewModel.ScrubbingChangeListener {
             override fun onScrubbingChanged(scrubbing: Boolean) {
-                if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+                if (!mediaFlags.isSceneContainerEnabled()) return
                 if (isScrubbing == scrubbing) return
                 isScrubbing = scrubbing
                 updateDisplayForScrubbingChange()
@@ -213,7 +213,7 @@
     private val enabledChangeListener =
         object : SeekBarViewModel.EnabledChangeListener {
             override fun onEnabledChanged(enabled: Boolean) {
-                if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+                if (!mediaFlags.isSceneContainerEnabled()) return
                 if (isSeekBarEnabled == enabled) return
                 isSeekBarEnabled = enabled
                 MediaControlViewBinder.updateSeekBarVisibility(expandedLayout, isSeekBarEnabled)
@@ -229,7 +229,7 @@
      * @param listening True when player should be active. Otherwise, false.
      */
     fun setListening(listening: Boolean) {
-        if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+        if (!mediaFlags.isSceneContainerEnabled()) return
         seekBarViewModel.listening = listening
     }
 
@@ -263,7 +263,7 @@
                             )
                         )
                     }
-                    if (mediaFlags.isMediaControlsRefactorEnabled()) {
+                    if (mediaFlags.isSceneContainerEnabled()) {
                         if (
                             this@MediaViewController::recsConfigurationChangeListener.isInitialized
                         ) {
@@ -305,6 +305,7 @@
      */
     var collapsedLayout = ConstraintSet()
         @VisibleForTesting set
+
     /**
      * The expanded constraint set used to render a collapsed player. If it is modified, make sure
      * to call [refreshState]
@@ -334,7 +335,7 @@
      * Notify this controller that the view has been removed and all listeners should be destroyed
      */
     fun onDestroy() {
-        if (mediaFlags.isMediaControlsRefactorEnabled()) {
+        if (mediaFlags.isSceneContainerEnabled()) {
             if (this::seekBarObserver.isInitialized) {
                 seekBarViewModel.progress.removeObserver(seekBarObserver)
             }
@@ -657,7 +658,7 @@
         }
 
     fun attachPlayer(mediaViewHolder: MediaViewHolder) {
-        if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+        if (!mediaFlags.isSceneContainerEnabled()) return
         this.mediaViewHolder = mediaViewHolder
 
         // Setting up seek bar.
@@ -731,7 +732,7 @@
     }
 
     fun updateAnimatorDurationScale() {
-        if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+        if (!mediaFlags.isSceneContainerEnabled()) return
         if (this::seekBarObserver.isInitialized) {
             seekBarObserver.animationEnabled =
                 globalSettings.getFloat(Settings.Global.ANIMATOR_DURATION_SCALE, 1f) > 0f
@@ -787,7 +788,7 @@
     }
 
     fun attachRecommendations(recommendationViewHolder: RecommendationViewHolder) {
-        if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+        if (!mediaFlags.isSceneContainerEnabled()) return
         this.recommendationViewHolder = recommendationViewHolder
 
         attach(recommendationViewHolder.recommendations, TYPE.RECOMMENDATION)
@@ -796,13 +797,13 @@
     }
 
     fun bindSeekBar(onSeek: () -> Unit, onBindSeekBar: (SeekBarViewModel) -> Unit) {
-        if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+        if (!mediaFlags.isSceneContainerEnabled()) return
         seekBarViewModel.logSeek = onSeek
         onBindSeekBar.invoke(seekBarViewModel)
     }
 
     fun setUpTurbulenceNoise() {
-        if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+        if (!mediaFlags.isSceneContainerEnabled()) return
         if (!this::turbulenceNoiseAnimationConfig.isInitialized) {
             turbulenceNoiseAnimationConfig =
                 createTurbulenceNoiseConfig(
@@ -1153,13 +1154,13 @@
     }
 
     fun setUpPrevButtonInfo(isAvailable: Boolean, notVisibleValue: Int = ConstraintSet.GONE) {
-        if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+        if (!mediaFlags.isSceneContainerEnabled()) return
         isPrevButtonAvailable = isAvailable
         prevNotVisibleValue = notVisibleValue
     }
 
     fun setUpNextButtonInfo(isAvailable: Boolean, notVisibleValue: Int = ConstraintSet.GONE) {
-        if (!mediaFlags.isMediaControlsRefactorEnabled()) return
+        if (!mediaFlags.isSceneContainerEnabled()) return
         isNextButtonAvailable = isAvailable
         nextNotVisibleValue = notVisibleValue
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
index 1e7bc0c..21c3111 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt
@@ -52,8 +52,4 @@
 
     /** Check whether to use scene framework */
     fun isSceneContainerEnabled() = SceneContainerFlag.isEnabled
-
-    /** Check whether to use media refactor code */
-    fun isMediaControlsRefactorEnabled() =
-        MediaControlsRefactorFlag.isEnabled && SceneContainerFlag.isEnabled
 }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt
index a618490..de56c84 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionCaptureTarget.kt
@@ -21,15 +21,17 @@
 import android.os.Parcelable
 
 /**
- * Class that represents an area that should be captured. Currently it has only a launch cookie that
- * represents a task but we potentially could add more identifiers e.g. for a pair of tasks.
+ * Class that represents an area that should be captured. Currently it has only a launch cookie and
+ * id that represents a task but we potentially could add more identifiers e.g. for a pair of tasks.
  */
-data class MediaProjectionCaptureTarget(val launchCookie: LaunchCookie?) : Parcelable {
+data class MediaProjectionCaptureTarget(val launchCookie: LaunchCookie?, val taskId: Int) :
+    Parcelable {
 
-    constructor(parcel: Parcel) : this(LaunchCookie.readFromParcel(parcel))
+    constructor(parcel: Parcel) : this(LaunchCookie.readFromParcel(parcel), parcel.readInt())
 
     override fun writeToParcel(dest: Parcel, flags: Int) {
         LaunchCookie.writeToParcel(launchCookie, dest)
+        dest.writeInt(taskId)
     }
 
     override fun describeContents(): Int = 0
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamActivityProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionModule.kt
similarity index 60%
rename from packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamActivityProvider.kt
rename to packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionModule.kt
index b35b7f5..3489459 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/homecontrols/DreamActivityProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/MediaProjectionModule.kt
@@ -13,15 +13,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.systemui.dreams.homecontrols
 
-import android.app.Activity
-import android.service.dreams.DreamService
+package com.android.systemui.mediaprojection
 
-fun interface DreamActivityProvider {
-    /**
-     * Provides abstraction for getting the activity associated with a dream service, so that the
-     * activity can be mocked in tests.
-     */
-    fun getActivity(dreamService: DreamService): Activity?
+import com.android.systemui.mediaprojection.data.repository.MediaProjectionManagerRepository
+import com.android.systemui.mediaprojection.data.repository.MediaProjectionRepository
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface MediaProjectionModule {
+    @Binds fun mediaRepository(impl: MediaProjectionManagerRepository): MediaProjectionRepository
 }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
index 4685c5a..d6affd2 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorActivity.kt
@@ -174,7 +174,7 @@
         // is created and ready to be captured.
         val activityStarted =
             activityLauncher.startActivityAsUser(intent, userHandle, activityOptions.toBundle()) {
-                returnSelectedApp(launchCookie)
+                returnSelectedApp(launchCookie, taskId = -1)
             }
 
         // Rely on the ActivityManager to pop up a dialog regarding app suspension
@@ -232,7 +232,7 @@
         }
     }
 
-    override fun returnSelectedApp(launchCookie: LaunchCookie) {
+    override fun returnSelectedApp(launchCookie: LaunchCookie, taskId: Int) {
         taskSelected = true
         if (intent.hasExtra(EXTRA_CAPTURE_REGION_RESULT_RECEIVER)) {
             // The client requested to return the result in the result receiver instead of
@@ -242,7 +242,7 @@
                     EXTRA_CAPTURE_REGION_RESULT_RECEIVER,
                     ResultReceiver::class.java
                 ) as ResultReceiver
-            val captureRegion = MediaProjectionCaptureTarget(launchCookie)
+            val captureRegion = MediaProjectionCaptureTarget(launchCookie, taskId)
             val data = Bundle().apply { putParcelable(KEY_CAPTURE_TARGET, captureRegion) }
             resultReceiver.send(RESULT_OK, data)
             // TODO(b/279175710): Ensure consent result is always set here. Skipping this for now
@@ -255,6 +255,7 @@
             val projection = IMediaProjection.Stub.asInterface(mediaProjectionBinder)
 
             projection.setLaunchCookie(launchCookie)
+            projection.setTaskId(taskId)
 
             val intent = Intent()
             intent.putExtra(EXTRA_MEDIA_PROJECTION, projection.asBinder())
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
index f08bc17..9b1ca1e 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorComponent.kt
@@ -65,7 +65,7 @@
     subcomponents = [MediaProjectionAppSelectorComponent::class],
     includes = [MediaProjectionDevicePolicyModule::class]
 )
-interface MediaProjectionModule {
+interface MediaProjectionActivitiesModule {
     @Binds
     @IntoMap
     @ClassKey(MediaProjectionAppSelectorActivity::class)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorResultHandler.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorResultHandler.kt
index f204b3e..6857000 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorResultHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorResultHandler.kt
@@ -9,7 +9,10 @@
 interface MediaProjectionAppSelectorResultHandler {
     /**
      * Return selected app to the original caller of the media projection app picker.
-     * @param launchCookie launch cookie of the launched activity of the target app
+     * @param launchCookie launch cookie of the launched activity of the target app, always set
+     *     regardless of launching a new task or a recent task
+     * @param taskId id of the launched task of the target app, only set to a positive int when
+     *     launching a recent task, otherwise set to -1 by default
      */
-    fun returnSelectedApp(launchCookie: LaunchCookie)
+    fun returnSelectedApp(launchCookie: LaunchCookie, taskId: Int)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
index 9549ab1..46aa064 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt
@@ -144,10 +144,9 @@
         activityOptions.launchDisplayId = task.displayId
         activityOptions.setLaunchCookie(launchCookie)
 
-        val handleResult: () -> Unit = { resultHandler.returnSelectedApp(launchCookie)}
-
         val taskId = task.taskId
         val splitBounds = task.splitBounds
+        val handleResult: () -> Unit = { resultHandler.returnSelectedApp(launchCookie, taskId)}
 
         if (pssAppSelectorRecentsSplitScreen() &&
             task.isLaunchingInSplitScreen() &&
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/model/MediaProjectionState.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/model/MediaProjectionState.kt
rename to packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt
index cfbcaf9..1d5f6f5 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/model/MediaProjectionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.mediaprojection.taskswitcher.data.model
+package com.android.systemui.mediaprojection.data.model
 
 import android.app.ActivityManager.RunningTaskInfo
 
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt
similarity index 88%
rename from packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepository.kt
rename to packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt
index 74d1992..3ce0a1e0 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.mediaprojection.taskswitcher.data.repository
+package com.android.systemui.mediaprojection.data.repository
 
 import android.app.ActivityManager.RunningTaskInfo
 import android.media.projection.MediaProjectionInfo
@@ -24,20 +24,21 @@
 import android.view.ContentRecordingSession
 import android.view.ContentRecordingSession.RECORD_CONTENT_DISPLAY
 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.mediaprojection.MediaProjectionServiceHelper
-import com.android.systemui.mediaprojection.taskswitcher.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.taskswitcher.data.repository.TasksRepository
+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.SharingStarted
-import kotlinx.coroutines.flow.shareIn
+import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.withContext
 
@@ -88,7 +89,11 @@
                 mediaProjectionManager.addCallback(callback, handler)
                 awaitClose { mediaProjectionManager.removeCallback(callback) }
             }
-            .shareIn(scope = applicationScope, started = SharingStarted.Lazily, replay = 1)
+            .stateIn(
+                scope = applicationScope,
+                started = SharingStarted.Lazily,
+                initialValue = MediaProjectionState.NotProjecting,
+            )
 
     private suspend fun stateForSession(session: ContentRecordingSession?): MediaProjectionState {
         if (session == null) {
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionRepository.kt
rename to packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt
index e495466..21300db 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.mediaprojection.taskswitcher.data.repository
+package com.android.systemui.mediaprojection.data.repository
 
 import android.app.ActivityManager.RunningTaskInfo
-import com.android.systemui.mediaprojection.taskswitcher.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
 import kotlinx.coroutines.flow.Flow
 
 /** Represents a repository to retrieve and change data related to media projection. */
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherModule.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherModule.kt
index 22ad07e..eb38958 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherModule.kt
@@ -17,16 +17,11 @@
 package com.android.systemui.mediaprojection.taskswitcher
 
 import com.android.systemui.mediaprojection.taskswitcher.data.repository.ActivityTaskManagerTasksRepository
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.MediaProjectionManagerRepository
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.MediaProjectionRepository
 import com.android.systemui.mediaprojection.taskswitcher.data.repository.TasksRepository
 import dagger.Binds
 import dagger.Module
 
 @Module
 interface MediaProjectionTaskSwitcherModule {
-
-    @Binds fun mediaRepository(impl: MediaProjectionManagerRepository): MediaProjectionRepository
-
     @Binds fun tasksRepository(impl: ActivityTaskManagerTasksRepository): TasksRepository
 }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt
index eb9e6a5..c232d4d 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt
@@ -21,8 +21,8 @@
 import android.content.Intent
 import android.util.Log
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.mediaprojection.taskswitcher.data.model.MediaProjectionState
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.MediaProjectionRepository
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.repository.MediaProjectionRepository
 import com.android.systemui.mediaprojection.taskswitcher.data.repository.TasksRepository
 import com.android.systemui.mediaprojection.taskswitcher.domain.model.TaskSwitchState
 import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
index a256b59..e931f8f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt
@@ -18,10 +18,7 @@
 private const val TAG = "BackPanel"
 private const val DEBUG = false
 
-class BackPanel(
-        context: Context,
-        private val latencyTracker: LatencyTracker
-) : View(context) {
+class BackPanel(context: Context, private val latencyTracker: LatencyTracker) : View(context) {
 
     var arrowsPointLeft = false
         set(value) {
@@ -42,39 +39,39 @@
     // True if the panel is currently on the left of the screen
     var isLeftPanel = false
 
-    /**
-     * Used to track back arrow latency from [android.view.MotionEvent.ACTION_DOWN] to [onDraw]
-     */
+    /** Used to track back arrow latency from [android.view.MotionEvent.ACTION_DOWN] to [onDraw] */
     private var trackingBackArrowLatency = false
 
-    /**
-     * The length of the arrow measured horizontally. Used for animating [arrowPath]
-     */
-    private var arrowLength = AnimatedFloat(
+    /** The length of the arrow measured horizontally. Used for animating [arrowPath] */
+    private var arrowLength =
+        AnimatedFloat(
             name = "arrowLength",
             minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_PIXELS
-    )
+        )
 
     /**
      * The height of the arrow measured vertically from its center to its top (i.e. half the total
      * height). Used for animating [arrowPath]
      */
-    var arrowHeight = AnimatedFloat(
+    var arrowHeight =
+        AnimatedFloat(
             name = "arrowHeight",
             minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_ROTATION_DEGREES
-    )
+        )
 
-    val backgroundWidth = AnimatedFloat(
+    val backgroundWidth =
+        AnimatedFloat(
             name = "backgroundWidth",
             minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_PIXELS,
             minimumValue = 0f,
-    )
+        )
 
-    val backgroundHeight = AnimatedFloat(
+    val backgroundHeight =
+        AnimatedFloat(
             name = "backgroundHeight",
             minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_PIXELS,
             minimumValue = 0f,
-    )
+        )
 
     /**
      * Corners of the background closer to the edge of the screen (where the arrow appeared from).
@@ -88,17 +85,19 @@
      */
     val backgroundFarCornerRadius = AnimatedFloat("backgroundFarCornerRadius")
 
-    var scale = AnimatedFloat(
+    var scale =
+        AnimatedFloat(
             name = "scale",
             minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_SCALE,
             minimumValue = 0f
-    )
+        )
 
-    val scalePivotX = AnimatedFloat(
+    val scalePivotX =
+        AnimatedFloat(
             name = "scalePivotX",
             minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_PIXELS,
             minimumValue = backgroundWidth.pos / 2,
-    )
+        )
 
     /**
      * Left/right position of the background relative to the canvas. Also corresponds with the
@@ -107,21 +106,24 @@
      */
     var horizontalTranslation = AnimatedFloat(name = "horizontalTranslation")
 
-    var arrowAlpha = AnimatedFloat(
+    var arrowAlpha =
+        AnimatedFloat(
             name = "arrowAlpha",
             minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_ALPHA,
             minimumValue = 0f,
             maximumValue = 1f
-    )
+        )
 
-    val backgroundAlpha = AnimatedFloat(
+    val backgroundAlpha =
+        AnimatedFloat(
             name = "backgroundAlpha",
             minimumVisibleChange = SpringAnimation.MIN_VISIBLE_CHANGE_ALPHA,
             minimumValue = 0f,
             maximumValue = 1f
-    )
+        )
 
-    private val allAnimatedFloat = setOf(
+    private val allAnimatedFloat =
+        setOf(
             arrowLength,
             arrowHeight,
             backgroundWidth,
@@ -132,7 +134,7 @@
             horizontalTranslation,
             arrowAlpha,
             backgroundAlpha
-    )
+        )
 
     /**
      * Canvas vertical translation. How far up/down the arrow and background appear relative to the
@@ -140,43 +142,45 @@
      */
     var verticalTranslation = AnimatedFloat("verticalTranslation")
 
-    /**
-     * Use for drawing debug info. Can only be set if [DEBUG]=true
-     */
+    /** Use for drawing debug info. Can only be set if [DEBUG]=true */
     var drawDebugInfo: ((canvas: Canvas) -> Unit)? = null
         set(value) {
             if (DEBUG) field = value
         }
 
     internal fun updateArrowPaint(arrowThickness: Float) {
-
         arrowPaint.strokeWidth = arrowThickness
 
-        val isDeviceInNightTheme = resources.configuration.uiMode and
-                Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
+        val isDeviceInNightTheme =
+            resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
+                Configuration.UI_MODE_NIGHT_YES
 
-        arrowPaint.color = Utils.getColorAttrDefaultColor(context,
+        arrowPaint.color =
+            Utils.getColorAttrDefaultColor(
+                context,
                 if (isDeviceInNightTheme) {
                     com.android.internal.R.attr.materialColorOnSecondaryContainer
                 } else {
                     com.android.internal.R.attr.materialColorOnSecondaryFixed
                 }
-        )
+            )
 
-        arrowBackgroundPaint.color = Utils.getColorAttrDefaultColor(context,
+        arrowBackgroundPaint.color =
+            Utils.getColorAttrDefaultColor(
+                context,
                 if (isDeviceInNightTheme) {
                     com.android.internal.R.attr.materialColorSecondaryContainer
                 } else {
                     com.android.internal.R.attr.materialColorSecondaryFixedDim
                 }
-        )
+            )
     }
 
     inner class AnimatedFloat(
-            name: String,
-            private val minimumVisibleChange: Float? = null,
-            private val minimumValue: Float? = null,
-            private val maximumValue: Float? = null,
+        name: String,
+        private val minimumVisibleChange: Float? = null,
+        private val minimumValue: Float? = null,
+        private val maximumValue: Float? = null,
     ) {
 
         // The resting position when not stretched by a touch drag
@@ -207,19 +211,21 @@
         }
 
         init {
-            val floatProp = object : FloatPropertyCompat<AnimatedFloat>(name) {
-                override fun setValue(animatedFloat: AnimatedFloat, value: Float) {
-                    animatedFloat.pos = value
-                }
+            val floatProp =
+                object : FloatPropertyCompat<AnimatedFloat>(name) {
+                    override fun setValue(animatedFloat: AnimatedFloat, value: Float) {
+                        animatedFloat.pos = value
+                    }
 
-                override fun getValue(animatedFloat: AnimatedFloat): Float = animatedFloat.pos
-            }
-            animation = SpringAnimation(this, floatProp).apply {
-                spring = SpringForce()
-                this@AnimatedFloat.minimumValue?.let { setMinValue(it) }
-                this@AnimatedFloat.maximumValue?.let { setMaxValue(it) }
-                this@AnimatedFloat.minimumVisibleChange?.let { minimumVisibleChange = it }
-            }
+                    override fun getValue(animatedFloat: AnimatedFloat): Float = animatedFloat.pos
+                }
+            animation =
+                SpringAnimation(this, floatProp).apply {
+                    spring = SpringForce()
+                    this@AnimatedFloat.minimumValue?.let { setMinValue(it) }
+                    this@AnimatedFloat.maximumValue?.let { setMaxValue(it) }
+                    this@AnimatedFloat.minimumVisibleChange?.let { minimumVisibleChange = it }
+                }
         }
 
         fun snapTo(newPosition: Float) {
@@ -233,11 +239,10 @@
             snapTo(restingPosition)
         }
 
-
         fun stretchTo(
-                stretchAmount: Float,
-                startingVelocity: Float? = null,
-                springForce: SpringForce? = null
+            stretchAmount: Float,
+            startingVelocity: Float? = null,
+            springForce: SpringForce? = null
         ) {
             animation.apply {
                 startingVelocity?.let {
@@ -297,8 +302,8 @@
     }
 
     fun addAnimationEndListener(
-            animatedFloat: AnimatedFloat,
-            endListener: DelayedOnAnimationEndListener
+        animatedFloat: AnimatedFloat,
+        endListener: DelayedOnAnimationEndListener
     ): Boolean {
         return if (animatedFloat.isRunning) {
             animatedFloat.addEndListener(endListener)
@@ -314,51 +319,51 @@
     }
 
     fun setStretch(
-            horizontalTranslationStretchAmount: Float,
-            arrowStretchAmount: Float,
-            arrowAlphaStretchAmount: Float,
-            backgroundAlphaStretchAmount: Float,
-            backgroundWidthStretchAmount: Float,
-            backgroundHeightStretchAmount: Float,
-            edgeCornerStretchAmount: Float,
-            farCornerStretchAmount: Float,
-            fullyStretchedDimens: EdgePanelParams.BackIndicatorDimens
+        horizontalTranslationStretchAmount: Float,
+        arrowStretchAmount: Float,
+        arrowAlphaStretchAmount: Float,
+        backgroundAlphaStretchAmount: Float,
+        backgroundWidthStretchAmount: Float,
+        backgroundHeightStretchAmount: Float,
+        edgeCornerStretchAmount: Float,
+        farCornerStretchAmount: Float,
+        fullyStretchedDimens: EdgePanelParams.BackIndicatorDimens
     ) {
         horizontalTranslation.stretchBy(
-                finalPosition = fullyStretchedDimens.horizontalTranslation,
-                amount = horizontalTranslationStretchAmount
+            finalPosition = fullyStretchedDimens.horizontalTranslation,
+            amount = horizontalTranslationStretchAmount
         )
         arrowLength.stretchBy(
-                finalPosition = fullyStretchedDimens.arrowDimens.length,
-                amount = arrowStretchAmount
+            finalPosition = fullyStretchedDimens.arrowDimens.length,
+            amount = arrowStretchAmount
         )
         arrowHeight.stretchBy(
-                finalPosition = fullyStretchedDimens.arrowDimens.height,
-                amount = arrowStretchAmount
+            finalPosition = fullyStretchedDimens.arrowDimens.height,
+            amount = arrowStretchAmount
         )
         arrowAlpha.stretchBy(
-                finalPosition = fullyStretchedDimens.arrowDimens.alpha,
-                amount = arrowAlphaStretchAmount
+            finalPosition = fullyStretchedDimens.arrowDimens.alpha,
+            amount = arrowAlphaStretchAmount
         )
         backgroundAlpha.stretchBy(
-                finalPosition = fullyStretchedDimens.backgroundDimens.alpha,
-                amount = backgroundAlphaStretchAmount
+            finalPosition = fullyStretchedDimens.backgroundDimens.alpha,
+            amount = backgroundAlphaStretchAmount
         )
         backgroundWidth.stretchBy(
-                finalPosition = fullyStretchedDimens.backgroundDimens.width,
-                amount = backgroundWidthStretchAmount
+            finalPosition = fullyStretchedDimens.backgroundDimens.width,
+            amount = backgroundWidthStretchAmount
         )
         backgroundHeight.stretchBy(
-                finalPosition = fullyStretchedDimens.backgroundDimens.height,
-                amount = backgroundHeightStretchAmount
+            finalPosition = fullyStretchedDimens.backgroundDimens.height,
+            amount = backgroundHeightStretchAmount
         )
         backgroundEdgeCornerRadius.stretchBy(
-                finalPosition = fullyStretchedDimens.backgroundDimens.edgeCornerRadius,
-                amount = edgeCornerStretchAmount
+            finalPosition = fullyStretchedDimens.backgroundDimens.edgeCornerRadius,
+            amount = edgeCornerStretchAmount
         )
         backgroundFarCornerRadius.stretchBy(
-                finalPosition = fullyStretchedDimens.backgroundDimens.farCornerRadius,
-                amount = farCornerStretchAmount
+            finalPosition = fullyStretchedDimens.backgroundDimens.farCornerRadius,
+            amount = farCornerStretchAmount
         )
     }
 
@@ -373,8 +378,11 @@
     }
 
     fun popArrowAlpha(startingVelocity: Float, springForce: SpringForce? = null) {
-        arrowAlpha.stretchTo(stretchAmount = 0f, startingVelocity = startingVelocity,
-                springForce = springForce)
+        arrowAlpha.stretchTo(
+            stretchAmount = 0f,
+            startingVelocity = startingVelocity,
+            springForce = springForce
+        )
     }
 
     fun resetStretch() {
@@ -392,12 +400,10 @@
         backgroundFarCornerRadius.snapToRestingPosition()
     }
 
-    /**
-     * Updates resting arrow and background size not accounting for stretch
-     */
+    /** Updates resting arrow and background size not accounting for stretch */
     internal fun setRestingDimens(
-            restingParams: EdgePanelParams.BackIndicatorDimens,
-            animate: Boolean = true
+        restingParams: EdgePanelParams.BackIndicatorDimens,
+        animate: Boolean = true
     ) {
         horizontalTranslation.updateRestingPosition(restingParams.horizontalTranslation)
         scale.updateRestingPosition(restingParams.scale)
@@ -410,27 +416,29 @@
         backgroundWidth.updateRestingPosition(restingParams.backgroundDimens.width, animate)
         backgroundHeight.updateRestingPosition(restingParams.backgroundDimens.height, animate)
         backgroundEdgeCornerRadius.updateRestingPosition(
-                restingParams.backgroundDimens.edgeCornerRadius, animate
+            restingParams.backgroundDimens.edgeCornerRadius,
+            animate
         )
         backgroundFarCornerRadius.updateRestingPosition(
-                restingParams.backgroundDimens.farCornerRadius, animate
+            restingParams.backgroundDimens.farCornerRadius,
+            animate
         )
     }
 
     fun animateVertically(yPos: Float) = verticalTranslation.stretchTo(yPos)
 
     fun setSpring(
-            horizontalTranslation: SpringForce? = null,
-            verticalTranslation: SpringForce? = null,
-            scale: SpringForce? = null,
-            arrowLength: SpringForce? = null,
-            arrowHeight: SpringForce? = null,
-            arrowAlpha: SpringForce? = null,
-            backgroundAlpha: SpringForce? = null,
-            backgroundFarCornerRadius: SpringForce? = null,
-            backgroundEdgeCornerRadius: SpringForce? = null,
-            backgroundWidth: SpringForce? = null,
-            backgroundHeight: SpringForce? = null,
+        horizontalTranslation: SpringForce? = null,
+        verticalTranslation: SpringForce? = null,
+        scale: SpringForce? = null,
+        arrowLength: SpringForce? = null,
+        arrowHeight: SpringForce? = null,
+        arrowAlpha: SpringForce? = null,
+        backgroundAlpha: SpringForce? = null,
+        backgroundFarCornerRadius: SpringForce? = null,
+        backgroundEdgeCornerRadius: SpringForce? = null,
+        backgroundWidth: SpringForce? = null,
+        backgroundHeight: SpringForce? = null,
     ) {
         arrowLength?.let { this.arrowLength.spring = it }
         arrowHeight?.let { this.arrowHeight.spring = it }
@@ -459,26 +467,28 @@
 
         if (!isLeftPanel) canvas.scale(-1f, 1f, canvasWidth / 2.0f, 0f)
 
-        canvas.translate(
-                horizontalTranslation.pos,
-                height * 0.5f + verticalTranslation.pos
-        )
+        canvas.translate(horizontalTranslation.pos, height * 0.5f + verticalTranslation.pos)
 
         canvas.scale(scale.pos, scale.pos, scalePivotX, 0f)
 
-        val arrowBackground = arrowBackgroundRect.apply {
-            left = 0f
-            top = -halfHeight
-            right = backgroundWidth
-            bottom = halfHeight
-        }.toPathWithRoundCorners(
-                topLeft = edgeCorner,
-                bottomLeft = edgeCorner,
-                topRight = farCorner,
-                bottomRight = farCorner
+        val arrowBackground =
+            arrowBackgroundRect
+                .apply {
+                    left = 0f
+                    top = -halfHeight
+                    right = backgroundWidth
+                    bottom = halfHeight
+                }
+                .toPathWithRoundCorners(
+                    topLeft = edgeCorner,
+                    bottomLeft = edgeCorner,
+                    topRight = farCorner,
+                    bottomRight = farCorner
+                )
+        canvas.drawPath(
+            arrowBackground,
+            arrowBackgroundPaint.apply { alpha = (255 * backgroundAlpha.pos).toInt() }
         )
-        canvas.drawPath(arrowBackground,
-                arrowBackgroundPaint.apply { alpha = (255 * backgroundAlpha.pos).toInt() })
 
         val dx = arrowLength.pos
         val dy = arrowHeight.pos
@@ -487,8 +497,8 @@
         // either the tip or the back of the arrow, whichever is closer
         val arrowOffset = (backgroundWidth - dx) / 2
         canvas.translate(
-                /* dx= */ arrowOffset,
-                /* dy= */ 0f /* pass 0 for the y position since the canvas was already translated */
+            /* dx= */ arrowOffset,
+            /* dy= */ 0f /* pass 0 for the y position since the canvas was already translated */
         )
 
         val arrowPointsAwayFromEdge = !arrowsPointLeft.xor(isLeftPanel)
@@ -500,8 +510,8 @@
         }
 
         val arrowPath = calculateArrowPath(dx = dx, dy = dy)
-        val arrowPaint = arrowPaint
-                .apply { alpha = (255 * min(arrowAlpha.pos, backgroundAlpha.pos)).toInt() }
+        val arrowPaint =
+            arrowPaint.apply { alpha = (255 * min(arrowAlpha.pos, backgroundAlpha.pos)).toInt() }
         canvas.drawPath(arrowPath, arrowPaint)
         canvas.restore()
 
@@ -519,17 +529,23 @@
     }
 
     private fun RectF.toPathWithRoundCorners(
-            topLeft: Float = 0f,
-            topRight: Float = 0f,
-            bottomRight: Float = 0f,
-            bottomLeft: Float = 0f
-    ): Path = Path().apply {
-        val corners = floatArrayOf(
-                topLeft, topLeft,
-                topRight, topRight,
-                bottomRight, bottomRight,
-                bottomLeft, bottomLeft
-        )
-        addRoundRect(this@toPathWithRoundCorners, corners, Path.Direction.CW)
-    }
-}
\ No newline at end of file
+        topLeft: Float = 0f,
+        topRight: Float = 0f,
+        bottomRight: Float = 0f,
+        bottomLeft: Float = 0f
+    ): Path =
+        Path().apply {
+            val corners =
+                floatArrayOf(
+                    topLeft,
+                    topLeft,
+                    topRight,
+                    topRight,
+                    bottomRight,
+                    bottomRight,
+                    bottomLeft,
+                    bottomLeft
+                )
+            addRoundRect(this@toPathWithRoundCorners, corners, Path.Direction.CW)
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index f8086f5..18358a7 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -27,7 +27,6 @@
 import android.view.HapticFeedbackConstants
 import android.view.MotionEvent
 import android.view.VelocityTracker
-import android.view.View
 import android.view.ViewConfiguration
 import android.view.WindowManager
 import androidx.annotation.VisibleForTesting
@@ -37,11 +36,12 @@
 import com.android.internal.jank.Cuj
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.util.LatencyTracker
-import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.NavigationEdgeBackPlugin
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.ViewController
+import com.android.systemui.util.concurrency.BackPanelUiThread
+import com.android.systemui.util.concurrency.UiThreadContext
 import com.android.systemui.util.time.SystemClock
 import java.io.PrintWriter
 import javax.inject.Inject
@@ -85,11 +85,11 @@
     context: Context,
     private val windowManager: WindowManager,
     private val viewConfiguration: ViewConfiguration,
-    @Main private val mainHandler: Handler,
+    private val mainHandler: Handler,
     private val systemClock: SystemClock,
     private val vibratorHelper: VibratorHelper,
     private val configurationController: ConfigurationController,
-    private val latencyTracker: LatencyTracker,
+    latencyTracker: LatencyTracker,
     private val interactionJankMonitor: InteractionJankMonitor,
 ) : ViewController<BackPanel>(BackPanel(context, latencyTracker)), NavigationEdgeBackPlugin {
 
@@ -104,7 +104,7 @@
     constructor(
         private val windowManager: WindowManager,
         private val viewConfiguration: ViewConfiguration,
-        @Main private val mainHandler: Handler,
+        @BackPanelUiThread private val uiThreadContext: UiThreadContext,
         private val systemClock: SystemClock,
         private val vibratorHelper: VibratorHelper,
         private val configurationController: ConfigurationController,
@@ -113,20 +113,19 @@
     ) {
         /** Construct a [BackPanelController]. */
         fun create(context: Context): BackPanelController {
-            val backPanelController =
-                BackPanelController(
+            uiThreadContext.isCurrentThread()
+            return BackPanelController(
                     context,
                     windowManager,
                     viewConfiguration,
-                    mainHandler,
+                    uiThreadContext.handler,
                     systemClock,
                     vibratorHelper,
                     configurationController,
                     latencyTracker,
                     interactionJankMonitor
                 )
-            backPanelController.init()
-            return backPanelController
+                .also { it.init() }
         }
     }
 
@@ -164,6 +163,7 @@
 
     private val elapsedTimeSinceInactive
         get() = systemClock.uptimeMillis() - gestureInactiveTime
+
     private val elapsedTimeSinceEntry
         get() = systemClock.uptimeMillis() - gestureEntryTime
 
@@ -612,6 +612,7 @@
     }
 
     private var previousPreThresholdWidthInterpolator = params.entryWidthInterpolator
+
     private fun preThresholdWidthStretchAmount(progress: Float): Float {
         val interpolator = run {
             val isPastSlop = totalTouchDeltaInactive > viewConfiguration.scaledTouchSlop
@@ -677,8 +678,7 @@
             velocityTracker?.run {
                 computeCurrentVelocity(PX_PER_SEC)
                 xVelocity.takeIf { mView.isLeftPanel } ?: (xVelocity * -1)
-            }
-                ?: 0f
+            } ?: 0f
         val isPastFlingVelocityThreshold =
             flingVelocity > viewConfiguration.scaledMinimumFlingVelocity
         return flingDistance > minFlingDistance && isPastFlingVelocityThreshold
@@ -1006,15 +1006,15 @@
 
     private fun performDeactivatedHapticFeedback() {
         vibratorHelper.performHapticFeedback(
-                mView,
-                HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE
+            mView,
+            HapticFeedbackConstants.GESTURE_THRESHOLD_DEACTIVATE
         )
     }
 
     private fun performActivatedHapticFeedback() {
         vibratorHelper.performHapticFeedback(
-                mView,
-                HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE
+            mView,
+            HapticFeedbackConstants.GESTURE_THRESHOLD_ACTIVATE
         )
     }
 
@@ -1028,8 +1028,7 @@
             velocityTracker?.run {
                 computeCurrentVelocity(PX_PER_MS)
                 MathUtils.smoothStep(slowVelocityBound, fastVelocityBound, abs(xVelocity))
-            }
-                ?: valueOnFastVelocity
+            } ?: valueOnFastVelocity
 
         return MathUtils.lerp(valueOnFastVelocity, valueOnSlowVelocity, 1 - factor)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index d0f8412..41cd2c4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -44,8 +44,6 @@
 import android.graphics.Region;
 import android.hardware.input.InputManager;
 import android.icu.text.SimpleDateFormat;
-import android.os.Handler;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
@@ -55,7 +53,6 @@
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.TypedValue;
-import android.view.Choreographer;
 import android.view.ISystemGestureExclusionListener;
 import android.view.IWindowManager;
 import android.view.InputDevice;
@@ -75,7 +72,6 @@
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.policy.GestureNavigationSettingsObserver;
 import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.plugins.FalsingManager;
@@ -94,7 +90,8 @@
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.util.Assert;
+import com.android.systemui.util.concurrency.BackPanelUiThread;
+import com.android.systemui.util.concurrency.UiThreadContext;
 import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.desktopmode.DesktopMode;
 import com.android.wm.shell.pip.Pip;
@@ -136,7 +133,7 @@
                 public void onSystemGestureExclusionChanged(int displayId,
                         Region systemGestureExclusion, Region unrestrictedOrNull) {
                     if (displayId == mDisplayId) {
-                        mMainExecutor.execute(() -> {
+                        mUiThreadContext.getExecutor().execute(() -> {
                             mExcludeRegion.set(systemGestureExclusion);
                             mUnrestrictedExcludeRegion.set(unrestrictedOrNull != null
                                     ? unrestrictedOrNull : systemGestureExclusion);
@@ -215,8 +212,7 @@
     private final Point mDisplaySize = new Point();
     private final int mDisplayId;
 
-    private final Executor mMainExecutor;
-    private final Handler mMainHandler;
+    private final UiThreadContext mUiThreadContext;
     private final Executor mBackgroundExecutor;
 
     private final Rect mPipExcludedBounds = new Rect();
@@ -411,8 +407,7 @@
             OverviewProxyService overviewProxyService,
             SysUiState sysUiState,
             PluginManager pluginManager,
-            @Main Executor executor,
-            @Main Handler handler,
+            @BackPanelUiThread UiThreadContext uiThreadContext,
             @Background Executor backgroundExecutor,
             UserTracker userTracker,
             NavigationModeController navigationModeController,
@@ -428,8 +423,7 @@
             Provider<LightBarController> lightBarControllerProvider) {
         mContext = context;
         mDisplayId = context.getDisplayId();
-        mMainExecutor = executor;
-        mMainHandler = handler;
+        mUiThreadContext = uiThreadContext;
         mBackgroundExecutor = backgroundExecutor;
         mUserTracker = userTracker;
         mOverviewProxyService = overviewProxyService;
@@ -478,7 +472,7 @@
                 ViewConfiguration.getLongPressTimeout());
 
         mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(
-                mMainHandler, mContext, this::onNavigationSettingsChanged);
+                mUiThreadContext.getHandler(), mContext, this::onNavigationSettingsChanged);
 
         updateCurrentUserResources();
     }
@@ -506,11 +500,13 @@
         final boolean previousForcedVisible = mIsButtonForcedVisible;
         mIsButtonForcedVisible =
                 mGestureNavigationSettingsObserver.areNavigationButtonForcedVisible();
+        // Update this before calling mButtonForcedVisibleCallback since NavigationBar will relayout
+        // and query isHandlingGestures() as a part of the callback
+        mIsBackGestureAllowed = !mIsButtonForcedVisible;
         if (previousForcedVisible != mIsButtonForcedVisible
                 && mButtonForcedVisibleCallback != null) {
             mButtonForcedVisibleCallback.accept(mIsButtonForcedVisible);
         }
-        mIsBackGestureAllowed = !mIsButtonForcedVisible;
 
         final DisplayMetrics dm = res.getDisplayMetrics();
         final float defaultGestureHeight = res.getDimension(
@@ -564,13 +560,15 @@
         mIsAttached = true;
         mOverviewProxyService.addCallback(mQuickSwitchListener);
         mSysUiState.addCallback(mSysUiStateCallback);
-        mInputManager.registerInputDeviceListener(mInputDeviceListener, mMainHandler);
-        int [] inputDevices = mInputManager.getInputDeviceIds();
+        mInputManager.registerInputDeviceListener(
+                mInputDeviceListener,
+                mUiThreadContext.getHandler());
+        int[] inputDevices = mInputManager.getInputDeviceIds();
         for (int inputDeviceId : inputDevices) {
             mInputDeviceListener.onInputDeviceAdded(inputDeviceId);
         }
         updateIsEnabled();
-        mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
+        mUserTracker.addCallback(mUserChangedCallback, mUiThreadContext.getExecutor());
     }
 
     /**
@@ -617,6 +615,10 @@
     }
 
     private void updateIsEnabled() {
+        mUiThreadContext.runWithScissors(this::updateIsEnabledInner);
+    }
+
+    private void updateIsEnabledInner() {
         try {
             Trace.beginSection("EdgeBackGestureHandler#updateIsEnabled");
 
@@ -661,12 +663,12 @@
                 TaskStackChangeListeners.getInstance().registerTaskStackListener(
                         mTaskStackListener);
                 DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
-                        mMainExecutor::execute, mOnPropertiesChangedListener);
+                        mUiThreadContext.getExecutor()::execute, mOnPropertiesChangedListener);
                 mPipOptional.ifPresent(pip -> pip.setOnIsInPipStateChangedListener(
                         mOnIsInPipStateChangedListener));
                 mDesktopModeOptional.ifPresent(
                         dm -> dm.addDesktopGestureExclusionRegionListener(
-                                mDesktopCornersChangedListener, mMainExecutor));
+                                mDesktopCornersChangedListener, mUiThreadContext.getExecutor()));
 
                 try {
                     mWindowManagerService.registerSystemGestureExclusionListener(
@@ -677,8 +679,8 @@
 
                 // Register input event receiver
                 mInputMonitor = new InputMonitorCompat("edge-swipe", mDisplayId);
-                mInputEventReceiver = mInputMonitor.getInputReceiver(Looper.getMainLooper(),
-                        Choreographer.getInstance(), this::onInputEvent);
+                mInputEventReceiver = mInputMonitor.getInputReceiver(mUiThreadContext.getLooper(),
+                        mUiThreadContext.getChoreographer(), this::onInputEvent);
 
                 // Add a nav bar panel window
                 resetEdgeBackPlugin();
@@ -773,7 +775,7 @@
         mUseMLModel = newState;
 
         if (mUseMLModel) {
-            Assert.isMainThread();
+            mUiThreadContext.isCurrentThread();
             if (mMLModelIsLoading) {
                 Log.d(TAG, "Model tried to load while already loading.");
                 return;
@@ -804,12 +806,13 @@
         }
         BackGestureTfClassifierProvider finalProvider = provider;
         Map<String, Integer> finalVocab = vocab;
-        mMainExecutor.execute(() -> onMLModelLoadFinished(finalProvider, finalVocab, threshold));
+        mUiThreadContext.getExecutor().execute(
+                () -> onMLModelLoadFinished(finalProvider, finalVocab, threshold));
     }
 
     private void onMLModelLoadFinished(BackGestureTfClassifierProvider provider,
             Map<String, Integer> vocab, float threshold) {
-        Assert.isMainThread();
+        mUiThreadContext.isCurrentThread();
         mMLModelIsLoading = false;
         if (!mUseMLModel) {
             // This can happen if the user disables Gesture Nav while the model is loading.
@@ -1291,7 +1294,7 @@
         updateBackAnimationThresholds();
         if (mLightBarControllerProvider.get() != null) {
             mBackAnimation.setStatusBarCustomizer((appearance) -> {
-                mMainExecutor.execute(() ->
+                mUiThreadContext.getExecutor().execute(() ->
                         mLightBarControllerProvider.get()
                                 .customizeStatusBarAppearance(appearance));
             });
@@ -1308,8 +1311,7 @@
         private final OverviewProxyService mOverviewProxyService;
         private final SysUiState mSysUiState;
         private final PluginManager mPluginManager;
-        private final Executor mExecutor;
-        private final Handler mHandler;
+        private final UiThreadContext mUiThreadContext;
         private final Executor mBackgroundExecutor;
         private final UserTracker mUserTracker;
         private final NavigationModeController mNavigationModeController;
@@ -1327,29 +1329,27 @@
 
         @Inject
         public Factory(OverviewProxyService overviewProxyService,
-                       SysUiState sysUiState,
-                       PluginManager pluginManager,
-                       @Main Executor executor,
-                       @Main Handler handler,
-                       @Background Executor backgroundExecutor,
-                       UserTracker userTracker,
-                       NavigationModeController navigationModeController,
-                       BackPanelController.Factory backPanelControllerFactory,
-                       ViewConfiguration viewConfiguration,
-                       WindowManager windowManager,
-                       IWindowManager windowManagerService,
-                       InputManager inputManager,
-                       Optional<Pip> pipOptional,
-                       Optional<DesktopMode> desktopModeOptional,
-                       FalsingManager falsingManager,
-                       Provider<BackGestureTfClassifierProvider>
-                               backGestureTfClassifierProviderProvider,
-                       Provider<LightBarController> lightBarControllerProvider) {
+                        SysUiState sysUiState,
+                        PluginManager pluginManager,
+                        @BackPanelUiThread UiThreadContext uiThreadContext,
+                        @Background Executor backgroundExecutor,
+                        UserTracker userTracker,
+                        NavigationModeController navigationModeController,
+                        BackPanelController.Factory backPanelControllerFactory,
+                        ViewConfiguration viewConfiguration,
+                        WindowManager windowManager,
+                        IWindowManager windowManagerService,
+                        InputManager inputManager,
+                        Optional<Pip> pipOptional,
+                        Optional<DesktopMode> desktopModeOptional,
+                        FalsingManager falsingManager,
+                        Provider<BackGestureTfClassifierProvider>
+                                backGestureTfClassifierProviderProvider,
+                        Provider<LightBarController> lightBarControllerProvider) {
             mOverviewProxyService = overviewProxyService;
             mSysUiState = sysUiState;
             mPluginManager = pluginManager;
-            mExecutor = executor;
-            mHandler = handler;
+            mUiThreadContext = uiThreadContext;
             mBackgroundExecutor = backgroundExecutor;
             mUserTracker = userTracker;
             mNavigationModeController = navigationModeController;
@@ -1367,26 +1367,26 @@
 
         /** Construct a {@link EdgeBackGestureHandler}. */
         public EdgeBackGestureHandler create(Context context) {
-            return new EdgeBackGestureHandler(
-                    context,
-                    mOverviewProxyService,
-                    mSysUiState,
-                    mPluginManager,
-                    mExecutor,
-                    mHandler,
-                    mBackgroundExecutor,
-                    mUserTracker,
-                    mNavigationModeController,
-                    mBackPanelControllerFactory,
-                    mViewConfiguration,
-                    mWindowManager,
-                    mWindowManagerService,
-                    mInputManager,
-                    mPipOptional,
-                    mDesktopModeOptional,
-                    mFalsingManager,
-                    mBackGestureTfClassifierProviderProvider,
-                    mLightBarControllerProvider);
+            return mUiThreadContext.runWithScissors(
+                    () -> new EdgeBackGestureHandler(
+                            context,
+                            mOverviewProxyService,
+                            mSysUiState,
+                            mPluginManager,
+                            mUiThreadContext,
+                            mBackgroundExecutor,
+                            mUserTracker,
+                            mNavigationModeController,
+                            mBackPanelControllerFactory,
+                            mViewConfiguration,
+                            mWindowManager,
+                            mWindowManagerService,
+                            mInputManager,
+                            mPipOptional,
+                            mDesktopModeOptional,
+                            mFalsingManager,
+                            mBackGestureTfClassifierProviderProvider,
+                            mLightBarControllerProvider));
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
index 439b7e1..db8749f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt
@@ -10,92 +10,114 @@
 data class EdgePanelParams(private var resources: Resources) {
 
     data class ArrowDimens(
-            val length: Float? = 0f,
-            val height: Float? = 0f,
-            val alpha: Float = 0f,
-            val heightSpring: SpringForce? = null,
-            val lengthSpring: SpringForce? = null,
-            var alphaSpring: Step<SpringForce>? = null,
-            var alphaInterpolator: Step<Float>? = null
+        val length: Float? = 0f,
+        val height: Float? = 0f,
+        val alpha: Float = 0f,
+        val heightSpring: SpringForce? = null,
+        val lengthSpring: SpringForce? = null,
+        var alphaSpring: Step<SpringForce>? = null,
+        var alphaInterpolator: Step<Float>? = null
     )
 
     data class BackgroundDimens(
-            val width: Float? = 0f,
-            val height: Float = 0f,
-            val edgeCornerRadius: Float = 0f,
-            val farCornerRadius: Float = 0f,
-            val alpha: Float = 0f,
-            val widthSpring: SpringForce? = null,
-            val heightSpring: SpringForce? = null,
-            val farCornerRadiusSpring: SpringForce? = null,
-            val edgeCornerRadiusSpring: SpringForce? = null,
-            val alphaSpring: SpringForce? = null,
+        val width: Float? = 0f,
+        val height: Float = 0f,
+        val edgeCornerRadius: Float = 0f,
+        val farCornerRadius: Float = 0f,
+        val alpha: Float = 0f,
+        val widthSpring: SpringForce? = null,
+        val heightSpring: SpringForce? = null,
+        val farCornerRadiusSpring: SpringForce? = null,
+        val edgeCornerRadiusSpring: SpringForce? = null,
+        val alphaSpring: SpringForce? = null,
     )
 
     data class BackIndicatorDimens(
-            val horizontalTranslation: Float? = 0f,
-            val scale: Float = 0f,
-            val scalePivotX: Float? = null,
-            val arrowDimens: ArrowDimens,
-            val backgroundDimens: BackgroundDimens,
-            val verticalTranslationSpring: SpringForce? = null,
-            val horizontalTranslationSpring: SpringForce? = null,
-            val scaleSpring: SpringForce? = null,
+        val horizontalTranslation: Float? = 0f,
+        val scale: Float = 0f,
+        val scalePivotX: Float? = null,
+        val arrowDimens: ArrowDimens,
+        val backgroundDimens: BackgroundDimens,
+        val verticalTranslationSpring: SpringForce? = null,
+        val horizontalTranslationSpring: SpringForce? = null,
+        val scaleSpring: SpringForce? = null,
     )
 
     lateinit var entryIndicator: BackIndicatorDimens
         private set
+
     lateinit var activeIndicator: BackIndicatorDimens
         private set
+
     lateinit var cancelledIndicator: BackIndicatorDimens
         private set
+
     lateinit var flungIndicator: BackIndicatorDimens
         private set
+
     lateinit var committedIndicator: BackIndicatorDimens
         private set
+
     lateinit var preThresholdIndicator: BackIndicatorDimens
         private set
+
     lateinit var fullyStretchedIndicator: BackIndicatorDimens
         private set
 
     // navigation bar edge constants
     var arrowPaddingEnd: Int = 0
         private set
+
     var arrowThickness: Float = 0f
         private set
+
     // The closest to y
     var minArrowYPosition: Int = 0
         private set
+
     var fingerOffset: Int = 0
         private set
+
     var staticTriggerThreshold: Float = 0f
         private set
+
     var reactivationTriggerThreshold: Float = 0f
         private set
+
     var deactivationTriggerThreshold: Float = 0f
         get() = -field
         private set
+
     lateinit var dynamicTriggerThresholdRange: ClosedRange<Float>
         private set
+
     var swipeProgressThreshold: Float = 0f
         private set
 
     lateinit var entryWidthInterpolator: Interpolator
         private set
+
     lateinit var entryWidthTowardsEdgeInterpolator: Interpolator
         private set
+
     lateinit var activeWidthInterpolator: Interpolator
         private set
+
     lateinit var arrowAngleInterpolator: Interpolator
         private set
+
     lateinit var horizontalTranslationInterpolator: Interpolator
         private set
+
     lateinit var verticalTranslationInterpolator: Interpolator
         private set
+
     lateinit var farCornerInterpolator: Interpolator
         private set
+
     lateinit var edgeCornerInterpolator: Interpolator
         private set
+
     lateinit var heightInterpolator: Interpolator
         private set
 
@@ -108,7 +130,10 @@
     }
 
     private fun getDimenFloat(id: Int): Float {
-        return TypedValue().run { resources.getValue(id, this, true); float }
+        return TypedValue().run {
+            resources.getValue(id, this, true)
+            float
+        }
     }
 
     private fun getPx(id: Int): Int {
@@ -123,11 +148,10 @@
         fingerOffset = getPx(R.dimen.navigation_edge_finger_offset)
         staticTriggerThreshold = getDimen(R.dimen.navigation_edge_action_drag_threshold)
         reactivationTriggerThreshold =
-                getDimen(R.dimen.navigation_edge_action_reactivation_drag_threshold)
+            getDimen(R.dimen.navigation_edge_action_reactivation_drag_threshold)
         deactivationTriggerThreshold =
-                getDimen(R.dimen.navigation_edge_action_deactivation_drag_threshold)
-        dynamicTriggerThresholdRange =
-                reactivationTriggerThreshold..deactivationTriggerThreshold
+            getDimen(R.dimen.navigation_edge_action_deactivation_drag_threshold)
+        dynamicTriggerThresholdRange = reactivationTriggerThreshold..deactivationTriggerThreshold
         swipeProgressThreshold = getDimen(R.dimen.navigation_edge_action_progress_threshold)
 
         entryWidthInterpolator = PathInterpolator(.19f, 1.27f, .71f, .86f)
@@ -149,27 +173,31 @@
 
         val commonArrowDimensAlphaThreshold = .165f
         val commonArrowDimensAlphaFactor = 1.05f
-        val commonArrowDimensAlphaSpring = Step(
-            threshold = commonArrowDimensAlphaThreshold,
-            factor = commonArrowDimensAlphaFactor,
-            postThreshold = createSpring(180f, 0.9f),
-            preThreshold = createSpring(2000f, 0.6f)
-        )
-        val commonArrowDimensAlphaSpringInterpolator = Step(
-            threshold = commonArrowDimensAlphaThreshold,
-            factor = commonArrowDimensAlphaFactor,
-            postThreshold = 1f,
-            preThreshold = 0f
-        )
+        val commonArrowDimensAlphaSpring =
+            Step(
+                threshold = commonArrowDimensAlphaThreshold,
+                factor = commonArrowDimensAlphaFactor,
+                postThreshold = createSpring(180f, 0.9f),
+                preThreshold = createSpring(2000f, 0.6f)
+            )
+        val commonArrowDimensAlphaSpringInterpolator =
+            Step(
+                threshold = commonArrowDimensAlphaThreshold,
+                factor = commonArrowDimensAlphaFactor,
+                postThreshold = 1f,
+                preThreshold = 0f
+            )
 
-        entryIndicator = BackIndicatorDimens(
+        entryIndicator =
+            BackIndicatorDimens(
                 horizontalTranslation = getDimen(R.dimen.navigation_edge_entry_margin),
                 scale = getDimenFloat(R.dimen.navigation_edge_entry_scale),
                 scalePivotX = getDimen(R.dimen.navigation_edge_pre_threshold_background_width),
                 horizontalTranslationSpring = createSpring(800f, 0.76f),
                 verticalTranslationSpring = createSpring(30000f, 1f),
                 scaleSpring = createSpring(120f, 0.8f),
-                arrowDimens = ArrowDimens(
+                arrowDimens =
+                    ArrowDimens(
                         length = getDimen(R.dimen.navigation_edge_entry_arrow_length),
                         height = getDimen(R.dimen.navigation_edge_entry_arrow_height),
                         alpha = 0f,
@@ -177,8 +205,9 @@
                         heightSpring = createSpring(600f, 0.4f),
                         alphaSpring = commonArrowDimensAlphaSpring,
                         alphaInterpolator = commonArrowDimensAlphaSpringInterpolator
-                ),
-                backgroundDimens = BackgroundDimens(
+                    ),
+                backgroundDimens =
+                    BackgroundDimens(
                         alpha = 1f,
                         width = getDimen(R.dimen.navigation_edge_entry_background_width),
                         height = getDimen(R.dimen.navigation_edge_entry_background_height),
@@ -188,16 +217,18 @@
                         heightSpring = createSpring(1500f, 0.45f),
                         farCornerRadiusSpring = createSpring(300f, 0.5f),
                         edgeCornerRadiusSpring = createSpring(150f, 0.5f),
-                )
-        )
+                    )
+            )
 
-        activeIndicator = BackIndicatorDimens(
+        activeIndicator =
+            BackIndicatorDimens(
                 horizontalTranslation = getDimen(R.dimen.navigation_edge_active_margin),
                 scale = getDimenFloat(R.dimen.navigation_edge_active_scale),
                 horizontalTranslationSpring = createSpring(1000f, 0.8f),
                 scaleSpring = createSpring(325f, 0.55f),
                 scalePivotX = getDimen(R.dimen.navigation_edge_active_background_width),
-                arrowDimens = ArrowDimens(
+                arrowDimens =
+                    ArrowDimens(
                         length = getDimen(R.dimen.navigation_edge_active_arrow_length),
                         height = getDimen(R.dimen.navigation_edge_active_arrow_height),
                         alpha = 1f,
@@ -205,8 +236,9 @@
                         heightSpring = activeCommittedArrowHeightSpring,
                         alphaSpring = commonArrowDimensAlphaSpring,
                         alphaInterpolator = commonArrowDimensAlphaSpringInterpolator
-                ),
-                backgroundDimens = BackgroundDimens(
+                    ),
+                backgroundDimens =
+                    BackgroundDimens(
                         alpha = 1f,
                         width = getDimen(R.dimen.navigation_edge_active_background_width),
                         height = getDimen(R.dimen.navigation_edge_active_background_height),
@@ -216,16 +248,18 @@
                         heightSpring = createSpring(10000f, 1f),
                         edgeCornerRadiusSpring = createSpring(2600f, 0.855f),
                         farCornerRadiusSpring = createSpring(1200f, 0.30f),
-                )
-        )
+                    )
+            )
 
-        preThresholdIndicator = BackIndicatorDimens(
+        preThresholdIndicator =
+            BackIndicatorDimens(
                 horizontalTranslation = getDimen(R.dimen.navigation_edge_pre_threshold_margin),
                 scale = getDimenFloat(R.dimen.navigation_edge_pre_threshold_scale),
                 scalePivotX = getDimen(R.dimen.navigation_edge_pre_threshold_background_width),
                 scaleSpring = createSpring(120f, 0.8f),
                 horizontalTranslationSpring = createSpring(6000f, 1f),
-                arrowDimens = ArrowDimens(
+                arrowDimens =
+                    ArrowDimens(
                         length = getDimen(R.dimen.navigation_edge_pre_threshold_arrow_length),
                         height = getDimen(R.dimen.navigation_edge_pre_threshold_arrow_height),
                         alpha = 1f,
@@ -233,32 +267,36 @@
                         heightSpring = createSpring(100f, 0.6f),
                         alphaSpring = commonArrowDimensAlphaSpring,
                         alphaInterpolator = commonArrowDimensAlphaSpringInterpolator
-                ),
-                backgroundDimens = BackgroundDimens(
+                    ),
+                backgroundDimens =
+                    BackgroundDimens(
                         alpha = 1f,
                         width = getDimen(R.dimen.navigation_edge_pre_threshold_background_width),
                         height = getDimen(R.dimen.navigation_edge_pre_threshold_background_height),
                         edgeCornerRadius =
-                                getDimen(R.dimen.navigation_edge_pre_threshold_edge_corners),
+                            getDimen(R.dimen.navigation_edge_pre_threshold_edge_corners),
                         farCornerRadius =
-                                getDimen(R.dimen.navigation_edge_pre_threshold_far_corners),
+                            getDimen(R.dimen.navigation_edge_pre_threshold_far_corners),
                         widthSpring = createSpring(650f, 1f),
                         heightSpring = createSpring(1500f, 0.45f),
                         farCornerRadiusSpring = createSpring(300f, 1f),
                         edgeCornerRadiusSpring = createSpring(250f, 0.5f),
-                )
-        )
+                    )
+            )
 
-        committedIndicator = activeIndicator.copy(
+        committedIndicator =
+            activeIndicator.copy(
                 horizontalTranslation = null,
                 scalePivotX = null,
-                arrowDimens = activeIndicator.arrowDimens.copy(
+                arrowDimens =
+                    activeIndicator.arrowDimens.copy(
                         lengthSpring = activeCommittedArrowLengthSpring,
                         heightSpring = activeCommittedArrowHeightSpring,
                         length = null,
                         height = null,
-                ),
-                backgroundDimens = activeIndicator.backgroundDimens.copy(
+                    ),
+                backgroundDimens =
+                    activeIndicator.backgroundDimens.copy(
                         alpha = 0f,
                         // explicitly set to null to preserve previous width upon state change
                         width = null,
@@ -267,49 +305,57 @@
                         edgeCornerRadiusSpring = flungCommittedEdgeCornerSpring,
                         farCornerRadiusSpring = flungCommittedFarCornerSpring,
                         alphaSpring = createSpring(1400f, 1f),
-                ),
+                    ),
                 scale = 0.86f,
                 scaleSpring = createSpring(5700f, 1f),
-        )
+            )
 
-        flungIndicator = committedIndicator.copy(
-                arrowDimens = committedIndicator.arrowDimens.copy(
+        flungIndicator =
+            committedIndicator.copy(
+                arrowDimens =
+                    committedIndicator.arrowDimens.copy(
                         lengthSpring = createSpring(850f, 0.46f),
                         heightSpring = createSpring(850f, 0.46f),
                         length = activeIndicator.arrowDimens.length,
                         height = activeIndicator.arrowDimens.height
-                ),
-                backgroundDimens = committedIndicator.backgroundDimens.copy(
+                    ),
+                backgroundDimens =
+                    committedIndicator.backgroundDimens.copy(
                         widthSpring = flungCommittedWidthSpring,
                         heightSpring = flungCommittedHeightSpring,
                         edgeCornerRadiusSpring = flungCommittedEdgeCornerSpring,
                         farCornerRadiusSpring = flungCommittedFarCornerSpring,
-                )
-        )
+                    )
+            )
 
-        cancelledIndicator = entryIndicator.copy(
-                backgroundDimens = entryIndicator.backgroundDimens.copy(
+        cancelledIndicator =
+            entryIndicator.copy(
+                backgroundDimens =
+                    entryIndicator.backgroundDimens.copy(
                         width = 0f,
                         alpha = 0f,
                         alphaSpring = createSpring(450f, 1f)
-                )
-        )
+                    )
+            )
 
-        fullyStretchedIndicator = BackIndicatorDimens(
+        fullyStretchedIndicator =
+            BackIndicatorDimens(
                 horizontalTranslation = getDimen(R.dimen.navigation_edge_stretch_margin),
                 scale = getDimenFloat(R.dimen.navigation_edge_stretch_scale),
                 horizontalTranslationSpring = null,
                 verticalTranslationSpring = null,
                 scaleSpring = null,
-                arrowDimens = ArrowDimens(
+                arrowDimens =
+                    ArrowDimens(
                         length = getDimen(R.dimen.navigation_edge_stretched_arrow_length),
                         height = getDimen(R.dimen.navigation_edge_stretched_arrow_height),
                         alpha = 1f,
                         alphaSpring = null,
                         heightSpring = null,
                         lengthSpring = null,
-                ),
-                backgroundDimens = BackgroundDimens(
+                    ),
+                backgroundDimens =
+                    BackgroundDimens(
                         alpha = 1f,
                         width = getDimen(R.dimen.navigation_edge_stretch_background_width),
                         height = getDimen(R.dimen.navigation_edge_stretch_background_height),
@@ -320,11 +366,11 @@
                         heightSpring = null,
                         edgeCornerRadiusSpring = null,
                         farCornerRadiusSpring = null,
-                )
-        )
+                    )
+            )
     }
 }
 
 fun createSpring(stiffness: Float, dampingRatio: Float): SpringForce {
     return SpringForce().setStiffness(stiffness).setDampingRatio(dampingRatio)
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt
index deb0fed..954e94a 100644
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.kt
@@ -26,11 +26,7 @@
 import androidx.lifecycle.lifecycleScope
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.compose.theme.PlatformTheme
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import com.android.systemui.people.ui.compose.PeopleScreen
-import com.android.systemui.people.ui.view.PeopleViewBinder
-import com.android.systemui.people.ui.view.PeopleViewBinder.bind
 import com.android.systemui.people.ui.viewmodel.PeopleViewModel
 import javax.inject.Inject
 import kotlinx.coroutines.launch
@@ -38,10 +34,7 @@
 /** People Tile Widget configuration activity that shows the user their conversation tiles. */
 class PeopleSpaceActivity
 @Inject
-constructor(
-    private val viewModelFactory: PeopleViewModel.Factory,
-    private val featureFlags: FeatureFlags,
-) : ComponentActivity() {
+constructor(private val viewModelFactory: PeopleViewModel.Factory) : ComponentActivity() {
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         setResult(RESULT_CANCELED)
@@ -66,17 +59,7 @@
         }
 
         // Set the content of the activity, using either the View or Compose implementation.
-        if (featureFlags.isEnabled(Flags.COMPOSE_PEOPLE_SPACE)) {
-            Log.d(TAG, "Using the Compose implementation of the PeopleSpaceActivity")
-            setContent {
-                PlatformTheme { PeopleScreen(viewModel, onResult = { finishActivity(it) }) }
-            }
-        } else {
-            Log.d(TAG, "Using the View implementation of the PeopleSpaceActivity")
-            val view = PeopleViewBinder.create(this)
-            bind(view, viewModel, lifecycleOwner = this, onResult = { finishActivity(it) })
-            setContentView(view)
-        }
+        setContent { PlatformTheme { PeopleScreen(viewModel, onResult = { finishActivity(it) }) } }
     }
 
     private fun finishActivity(result: PeopleViewModel.Result) {
diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java
deleted file mode 100644
index 59c76ad..0000000
--- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceTileView.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.people;
-
-import android.app.people.PeopleSpaceTile;
-import android.content.Context;
-import android.content.pm.LauncherApps;
-import android.graphics.Bitmap;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-import com.android.systemui.res.R;
-
-/**
- * PeopleSpaceTileView renders an individual person's tile with associated status.
- */
-public class PeopleSpaceTileView extends LinearLayout {
-
-    private View mTileView;
-    private TextView mNameView;
-    private ImageView mPersonIconView;
-
-    public PeopleSpaceTileView(Context context, ViewGroup view, String shortcutId, boolean isLast) {
-        super(context);
-        mTileView = view.findViewWithTag(shortcutId);
-        if (mTileView == null) {
-            LayoutInflater inflater = LayoutInflater.from(context);
-            mTileView = inflater.inflate(R.layout.people_space_tile_view, view, false);
-            view.addView(mTileView, LayoutParams.MATCH_PARENT,
-                    LayoutParams.MATCH_PARENT);
-            mTileView.setTag(shortcutId);
-
-            // If it's not the last conversation in this section, add a divider.
-            if (!isLast) {
-                inflater.inflate(R.layout.people_space_activity_list_divider, view, true);
-            }
-        }
-        mNameView = mTileView.findViewById(R.id.tile_view_name);
-        mPersonIconView = mTileView.findViewById(R.id.tile_view_person_icon);
-    }
-
-    /** Sets the name text on the tile. */
-    public void setName(String name) {
-        mNameView.setText(name);
-    }
-
-    /** Sets the person and package drawable on the tile. */
-    public void setPersonIcon(Bitmap bitmap) {
-        mPersonIconView.setImageBitmap(bitmap);
-    }
-
-    /** Sets the click listener of the tile. */
-    public void setOnClickListener(LauncherApps launcherApps, PeopleSpaceTile tile) {
-        mTileView.setOnClickListener(v ->
-                launcherApps.startShortcut(tile.getPackageName(), tile.getId(), null, null,
-                        tile.getUserHandle()));
-    }
-
-    /** Sets the click listener of the tile directly. */
-    public void setOnClickListener(OnClickListener onClickListener) {
-        mTileView.setOnClickListener(onClickListener);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt b/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt
deleted file mode 100644
index 10a2b3c..0000000
--- a/packages/SystemUI/src/com/android/systemui/people/ui/view/PeopleViewBinder.kt
+++ /dev/null
@@ -1,239 +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.people.ui.view
-
-import android.content.Context
-import android.graphics.Color
-import android.graphics.Outline
-import android.graphics.drawable.GradientDrawable
-import android.util.Log
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.view.ViewOutlineProvider
-import android.widget.LinearLayout
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.Lifecycle.State.CREATED
-import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.people.PeopleSpaceTileView
-import com.android.systemui.people.ui.viewmodel.PeopleTileViewModel
-import com.android.systemui.people.ui.viewmodel.PeopleViewModel
-import com.android.systemui.res.R
-import kotlinx.coroutines.flow.collect
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.launch
-
-/** A ViewBinder for [PeopleViewModel]. */
-object PeopleViewBinder {
-    private const val TAG = "PeopleViewBinder"
-
-    /**
-     * The [ViewOutlineProvider] used to clip the corner radius of the recent and priority lists.
-     */
-    private val ViewOutlineProvider =
-        object : ViewOutlineProvider() {
-            override fun getOutline(view: View, outline: Outline) {
-                outline.setRoundRect(
-                    0,
-                    0,
-                    view.width,
-                    view.height,
-                    view.context.resources.getDimension(R.dimen.people_space_widget_radius),
-                )
-            }
-        }
-
-    /** Create a [View] that can later be [bound][bind] to a [PeopleViewModel]. */
-    @JvmStatic
-    fun create(context: Context): ViewGroup {
-        return LayoutInflater.from(context)
-            .inflate(R.layout.people_space_activity, /* root= */ null) as ViewGroup
-    }
-
-    /** Bind [view] to [viewModel]. */
-    @JvmStatic
-    fun bind(
-        view: ViewGroup,
-        viewModel: PeopleViewModel,
-        lifecycleOwner: LifecycleOwner,
-        onResult: (PeopleViewModel.Result) -> Unit,
-    ) {
-        // Call [onResult] as soon as a result is available.
-        lifecycleOwner.lifecycleScope.launch {
-            lifecycleOwner.repeatOnLifecycle(CREATED) {
-                viewModel.result.collect { result ->
-                    if (result != null) {
-                        viewModel.clearResult()
-                        onResult(result)
-                    }
-                }
-            }
-        }
-
-        // Start collecting the UI data once the Activity is STARTED.
-        lifecycleOwner.lifecycleScope.launch {
-            lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
-                combine(
-                        viewModel.priorityTiles,
-                        viewModel.recentTiles,
-                    ) { priority, recent ->
-                        priority to recent
-                    }
-                    .collect { (priorityTiles, recentTiles) ->
-                        if (priorityTiles.isNotEmpty() || recentTiles.isNotEmpty()) {
-                            setConversationsContent(
-                                view,
-                                priorityTiles,
-                                recentTiles,
-                                viewModel.onTileClicked,
-                            )
-                        } else {
-                            setNoConversationsContent(view, viewModel.onUserJourneyCancelled)
-                        }
-                    }
-            }
-        }
-    }
-
-    private fun setNoConversationsContent(view: ViewGroup, onGotItClicked: () -> Unit) {
-        // This should never happen.
-        if (view.childCount > 1) {
-            error("view has ${view.childCount} children, it should have maximum 1")
-        }
-
-        // The static content for no conversations is already shown.
-        if (view.findViewById<View>(R.id.top_level_no_conversations) != null) {
-            return
-        }
-
-        // If we were showing the content with conversations earlier, remove it.
-        if (view.childCount == 1) {
-            view.removeViewAt(0)
-        }
-
-        val context = view.context
-        val noConversationsView =
-            LayoutInflater.from(context)
-                .inflate(R.layout.people_space_activity_no_conversations, /* root= */ view)
-
-        noConversationsView.requireViewById<View>(R.id.got_it_button).setOnClickListener {
-            onGotItClicked()
-        }
-
-        // The Tile preview has colorBackground as its background. Change it so it's different than
-        // the activity's background.
-        val item = noConversationsView.requireViewById<LinearLayout>(android.R.id.background)
-        val shape = item.background as GradientDrawable
-        val ta =
-            context.theme.obtainStyledAttributes(
-                intArrayOf(com.android.internal.R.attr.colorSurface)
-            )
-        shape.setColor(ta.getColor(0, Color.WHITE))
-        ta.recycle()
-    }
-
-    private fun setConversationsContent(
-        view: ViewGroup,
-        priorityTiles: List<PeopleTileViewModel>,
-        recentTiles: List<PeopleTileViewModel>,
-        onTileClicked: (PeopleTileViewModel) -> Unit,
-    ) {
-        // This should never happen.
-        if (view.childCount > 1) {
-            error("view has ${view.childCount} children, it should have maximum 1")
-        }
-
-        // Inflate the content with conversations, if it's not already.
-        if (view.findViewById<View>(R.id.top_level_with_conversations) == null) {
-            // If we were showing the content without conversations earlier, remove it.
-            if (view.childCount == 1) {
-                view.removeViewAt(0)
-            }
-
-            LayoutInflater.from(view.context)
-                .inflate(R.layout.people_space_activity_with_conversations, /* root= */ view)
-        }
-
-        // TODO(b/193782241): Replace the NestedScrollView + 2x LinearLayout from this layout into a
-        // single RecyclerView once this screen is tested by screenshot tests. Introduce a
-        // PeopleSpaceTileViewBinder that will properly create and bind the View associated to a
-        // PeopleSpaceTileViewModel (and remove the PeopleSpaceTileView class).
-        val conversationsView = view.requireViewById<View>(R.id.top_level_with_conversations)
-        setTileViews(
-            conversationsView,
-            R.id.priority,
-            R.id.priority_tiles,
-            priorityTiles,
-            onTileClicked,
-        )
-
-        setTileViews(
-            conversationsView,
-            R.id.recent,
-            R.id.recent_tiles,
-            recentTiles,
-            onTileClicked,
-        )
-    }
-
-    /** Sets a [PeopleSpaceTileView]s for each conversation. */
-    private fun setTileViews(
-        root: View,
-        tilesListId: Int,
-        tilesId: Int,
-        tiles: List<PeopleTileViewModel>,
-        onTileClicked: (PeopleTileViewModel) -> Unit,
-    ) {
-        // Remove any previously added tile.
-        // TODO(b/193782241): Once this list is a big RecyclerView, set the current list and use
-        // DiffUtil to do as less addView/removeView as possible.
-        val layout = root.requireViewById<ViewGroup>(tilesId)
-        layout.removeAllViews()
-        layout.outlineProvider = ViewOutlineProvider
-
-        val tilesListView = root.requireViewById<LinearLayout>(tilesListId)
-        if (tiles.isEmpty()) {
-            tilesListView.visibility = View.GONE
-            return
-        }
-        tilesListView.visibility = View.VISIBLE
-
-        // Add each tile.
-        tiles.forEachIndexed { i, tile ->
-            val tileView =
-                PeopleSpaceTileView(root.context, layout, tile.key.shortcutId, i == tiles.size - 1)
-            bindTileView(tileView, tile, onTileClicked)
-        }
-    }
-
-    /** Sets [tileView] with the data in [conversation]. */
-    private fun bindTileView(
-        tileView: PeopleSpaceTileView,
-        tile: PeopleTileViewModel,
-        onTileClicked: (PeopleTileViewModel) -> Unit,
-    ) {
-        try {
-            tileView.setName(tile.username)
-            tileView.setPersonIcon(tile.icon)
-            tileView.setOnClickListener { onTileClicked(tile) }
-        } catch (e: Exception) {
-            Log.e(TAG, "Couldn't retrieve shortcut information", e)
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java b/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java
index 2d460a0..765b45b 100644
--- a/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java
@@ -341,7 +341,7 @@
         }
 
         mQRCodeScannerPreferenceObserver.forEach((key, value) -> {
-            mSecureSettings.unregisterContentObserver(value);
+            mSecureSettings.unregisterContentObserverSync(value);
         });
 
         // Reset cached values to default as we are no longer listening
@@ -418,7 +418,7 @@
                 });
             }
         });
-        mSecureSettings.registerContentObserverForUser(
+        mSecureSettings.registerContentObserverForUserSync(
                 mSecureSettings.getUriFor(LOCK_SCREEN_SHOW_QR_CODE_SCANNER), false,
                 mQRCodeScannerPreferenceObserver.get(userId), userId);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
index 2fafba1..e4bafcd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
@@ -52,7 +52,9 @@
  *
  * It also handles restore gracefully.
  */
-class AutoAddTracker @VisibleForTesting constructor(
+class AutoAddTracker
+@VisibleForTesting
+constructor(
     private val secureSettings: SecureSettings,
     private val broadcastDispatcher: BroadcastDispatcher,
     private val qsHost: QSHost,
@@ -66,39 +68,43 @@
         private val FILTER = IntentFilter(Intent.ACTION_SETTING_RESTORED)
     }
 
-    @GuardedBy("autoAdded")
-    private val autoAdded = ArraySet<String>()
+    @GuardedBy("autoAdded") private val autoAdded = ArraySet<String>()
     private var restoredTiles: Map<String, AutoTile>? = null
 
     override val currentUserId: Int
         get() = userId
 
-    private val contentObserver = object : ContentObserver(mainHandler) {
-        override fun onChange(
-            selfChange: Boolean,
-            uris: Collection<Uri>,
-            flags: Int,
-            _userId: Int
-        ) {
-            if (_userId != userId) {
-                // Ignore changes outside of our user. We'll load the correct value on user change
-                return
+    private val contentObserver =
+        object : ContentObserver(mainHandler) {
+            override fun onChange(
+                selfChange: Boolean,
+                uris: Collection<Uri>,
+                flags: Int,
+                _userId: Int
+            ) {
+                if (_userId != userId) {
+                    // Ignore changes outside of our user. We'll load the correct value on user
+                    // change
+                    return
+                }
+                loadTiles()
             }
-            loadTiles()
         }
-    }
 
-    private val restoreReceiver = object : BroadcastReceiver() {
-        override fun onReceive(context: Context, intent: Intent) {
-            if (intent.action != Intent.ACTION_SETTING_RESTORED) return
-            processRestoreIntent(intent)
+    private val restoreReceiver =
+        object : BroadcastReceiver() {
+            override fun onReceive(context: Context, intent: Intent) {
+                if (intent.action != Intent.ACTION_SETTING_RESTORED) return
+                processRestoreIntent(intent)
+            }
         }
-    }
 
     private fun processRestoreIntent(intent: Intent) {
         when (intent.getStringExtra(Intent.EXTRA_SETTING_NAME)) {
             Settings.Secure.QS_TILES -> {
-                restoredTiles = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
+                restoredTiles =
+                    intent
+                        .getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
                         ?.split(DELIMITER)
                         ?.mapIndexed(::AutoTile)
                         ?.associateBy(AutoTile::tileType)
@@ -109,13 +115,11 @@
             }
             Settings.Secure.QS_AUTO_ADDED_TILES -> {
                 restoredTiles?.let { restoredTiles ->
-                    val restoredAutoAdded = intent
-                            .getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
-                            ?.split(DELIMITER)
+                    val restoredAutoAdded =
+                        intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)?.split(DELIMITER)
                             ?: emptyList()
-                    val autoAddedBeforeRestore = intent
-                            .getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE)
-                            ?.split(DELIMITER)
+                    val autoAddedBeforeRestore =
+                        intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE)?.split(DELIMITER)
                             ?: emptyList()
 
                     val tilesToRemove = restoredAutoAdded.filter { it !in restoredTiles }
@@ -123,50 +127,51 @@
                         Log.d(TAG, "Removing tiles: $tilesToRemove")
                         qsHost.removeTiles(tilesToRemove)
                     }
-                    val tiles = synchronized(autoAdded) {
-                        autoAdded.clear()
-                        autoAdded.addAll(restoredAutoAdded + autoAddedBeforeRestore)
-                        getTilesFromListLocked()
-                    }
+                    val tiles =
+                        synchronized(autoAdded) {
+                            autoAdded.clear()
+                            autoAdded.addAll(restoredAutoAdded + autoAddedBeforeRestore)
+                            getTilesFromListLocked()
+                        }
                     saveTiles(tiles)
-                } ?: run {
-                    Log.w(TAG, "${Settings.Secure.QS_AUTO_ADDED_TILES} restored before " +
-                            "${Settings.Secure.QS_TILES} for user $userId")
                 }
+                    ?: run {
+                        Log.w(
+                            TAG,
+                            "${Settings.Secure.QS_AUTO_ADDED_TILES} restored before " +
+                                "${Settings.Secure.QS_TILES} for user $userId"
+                        )
+                    }
             }
             else -> {} // Do nothing for other Settings
         }
     }
 
-    /**
-     * Init method must be called after construction to start listening
-     */
+    /** Init method must be called after construction to start listening */
     fun initialize() {
         dumpManager.registerDumpable(TAG, this)
         loadTiles()
-        secureSettings.registerContentObserverForUser(
-                secureSettings.getUriFor(Settings.Secure.QS_AUTO_ADDED_TILES),
-                contentObserver,
-                UserHandle.USER_ALL
+        secureSettings.registerContentObserverForUserSync(
+            secureSettings.getUriFor(Settings.Secure.QS_AUTO_ADDED_TILES),
+            contentObserver,
+            UserHandle.USER_ALL
         )
         registerBroadcastReceiver()
     }
 
-    /**
-     * Unregister listeners, receivers and observers
-     */
+    /** Unregister listeners, receivers and observers */
     fun destroy() {
         dumpManager.unregisterDumpable(TAG)
-        secureSettings.unregisterContentObserver(contentObserver)
+        secureSettings.unregisterContentObserverSync(contentObserver)
         unregisterBroadcastReceiver()
     }
 
     private fun registerBroadcastReceiver() {
         broadcastDispatcher.registerReceiver(
-                restoreReceiver,
-                FILTER,
-                backgroundExecutor,
-                UserHandle.of(userId)
+            restoreReceiver,
+            FILTER,
+            backgroundExecutor,
+            UserHandle.of(userId)
         )
     }
 
@@ -186,13 +191,9 @@
     fun getRestoredTilePosition(tile: String): Int =
         restoredTiles?.get(tile)?.index ?: QSHost.POSITION_AT_END
 
-    /**
-     * Returns `true` if the tile has been auto-added before
-     */
+    /** Returns `true` if the tile has been auto-added before */
     fun isAdded(tile: String): Boolean {
-        return synchronized(autoAdded) {
-            tile in autoAdded
-        }
+        return synchronized(autoAdded) { tile in autoAdded }
     }
 
     /**
@@ -201,13 +202,14 @@
      * From here on, [isAdded] will return true for that tile.
      */
     fun setTileAdded(tile: String) {
-        val tiles = synchronized(autoAdded) {
-            if (autoAdded.add(tile)) {
-                getTilesFromListLocked()
-            } else {
-                null
+        val tiles =
+            synchronized(autoAdded) {
+                if (autoAdded.add(tile)) {
+                    getTilesFromListLocked()
+                } else {
+                    null
+                }
             }
-        }
         tiles?.let { saveTiles(it) }
     }
 
@@ -217,13 +219,14 @@
      * This allows for this tile to be auto-added again in the future.
      */
     fun setTileRemoved(tile: String) {
-        val tiles = synchronized(autoAdded) {
-            if (autoAdded.remove(tile)) {
-                getTilesFromListLocked()
-            } else {
-                null
+        val tiles =
+            synchronized(autoAdded) {
+                if (autoAdded.remove(tile)) {
+                    getTilesFromListLocked()
+                } else {
+                    null
+                }
             }
-        }
         tiles?.let { saveTiles(it) }
     }
 
@@ -233,12 +236,12 @@
 
     private fun saveTiles(tiles: String) {
         secureSettings.putStringForUser(
-                Settings.Secure.QS_AUTO_ADDED_TILES,
-                tiles,
-                /* tag */ null,
-                /* makeDefault */ false,
-                userId,
-                /* overrideableByRestore */ true
+            Settings.Secure.QS_AUTO_ADDED_TILES,
+            tiles,
+            /* tag */ null,
+            /* makeDefault */ false,
+            userId,
+            /* overrideableByRestore */ true
         )
     }
 
@@ -261,7 +264,9 @@
     }
 
     @SysUISingleton
-    class Builder @Inject constructor(
+    class Builder
+    @Inject
+    constructor(
         private val secureSettings: SecureSettings,
         private val broadcastDispatcher: BroadcastDispatcher,
         private val qsHost: QSHost,
@@ -278,16 +283,16 @@
 
         fun build(): AutoAddTracker {
             return AutoAddTracker(
-                    secureSettings,
-                    broadcastDispatcher,
-                    qsHost,
-                    dumpManager,
-                    handler,
-                    executor,
-                    userId
+                secureSettings,
+                broadcastDispatcher,
+                qsHost,
+                dumpManager,
+                handler,
+                executor,
+                userId
             )
         }
     }
 
     private data class AutoTile(val index: Int, val tileType: String)
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index b34b370..e77bd03 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -485,6 +485,11 @@
     }
 
     @Override
+    public int getMinRows() {
+        return mMinRows;
+    }
+
+    @Override
     public boolean setMaxColumns(int maxColumns) {
         mMaxColumns = maxColumns;
         boolean changed = false;
@@ -497,6 +502,11 @@
         return changed;
     }
 
+    @Override
+    public int getMaxColumns() {
+        return mMaxColumns;
+    }
+
     /**
      * Set the amount of excess space that we gave this view compared to the actual available
      * height. This is because this view is in a scrollview.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 4ee2db7..cc0901f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -307,7 +307,7 @@
             } else {
                 // Set the horizontal paddings unless the view is the Compose implementation of the
                 // footer actions.
-                if (view.getTag(R.id.tag_compose_qs_footer_actions) == null) {
+                if (view.getId() != R.id.qs_footer_actions) {
                     view.setPaddingRelative(
                             mContentHorizontalPadding,
                             view.getPaddingTop(),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
index e424975..38d7290 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentLegacy.java
@@ -219,6 +219,13 @@
     }
 
     @Override
+    public void setShouldUpdateSquishinessOnMedia(boolean shouldUpdate) {
+        if (mQsImpl != null) {
+            mQsImpl.setShouldUpdateSquishinessOnMedia(shouldUpdate);
+        }
+    }
+
+    @Override
     public void setListening(boolean listening) {
         if (mQsImpl != null) {
             mQsImpl.setListening(listening);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
index 1f4838e..8c0d122 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSImpl.java
@@ -34,11 +34,11 @@
 import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.LinearLayout;
 
 import androidx.annotation.FloatRange;
 import androidx.annotation.Nullable;
 import androidx.annotation.VisibleForTesting;
+import androidx.compose.ui.platform.ComposeView;
 import androidx.lifecycle.Lifecycle;
 import androidx.lifecycle.LifecycleOwner;
 import androidx.lifecycle.LifecycleRegistry;
@@ -48,15 +48,12 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.animation.ShadeInterpolation;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.media.controls.ui.view.MediaHost;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.qs.QSContainerController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.customize.QSCustomizerController;
 import com.android.systemui.qs.dagger.QSComponent;
-import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder;
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.res.R;
@@ -117,11 +114,9 @@
     private final MediaHost mQqsMediaHost;
     private final QSDisableFlagsLogger mQsDisableFlagsLogger;
     private final LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
-    private final FeatureFlags mFeatureFlags;
     private final QSLogger mLogger;
     private final FooterActionsController mFooterActionsController;
     private final FooterActionsViewModel.Factory mFooterActionsViewModelFactory;
-    private final FooterActionsViewBinder mFooterActionsViewBinder;
     private final ListeningAndVisibilityLifecycleOwner mListeningAndVisibilityLifecycleOwner;
     private boolean mShowCollapsedOnKeyguard;
     private boolean mLastKeyguardAndExpanded;
@@ -168,11 +163,14 @@
 
     private boolean mIsSmallScreen;
 
+    /** Should the squishiness fraction be updated on the media host. */
+    private boolean mShouldUpdateMediaSquishiness;
+
     private CommandQueue mCommandQueue;
 
     private View mRootView;
     @Nullable
-    private View mFooterActionsView;
+    private ComposeView mFooterActionsView;
 
     @Inject
     public QSImpl(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
@@ -184,23 +182,19 @@
             DumpManager dumpManager, QSLogger qsLogger,
             FooterActionsController footerActionsController,
             FooterActionsViewModel.Factory footerActionsViewModelFactory,
-            FooterActionsViewBinder footerActionsViewBinder,
-            LargeScreenShadeInterpolator largeScreenShadeInterpolator,
-            FeatureFlags featureFlags) {
+            LargeScreenShadeInterpolator largeScreenShadeInterpolator) {
         mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
         mQsMediaHost = qsMediaHost;
         mQqsMediaHost = qqsMediaHost;
         mQsDisableFlagsLogger = qsDisableFlagsLogger;
         mLogger = qsLogger;
         mLargeScreenShadeInterpolator = largeScreenShadeInterpolator;
-        mFeatureFlags = featureFlags;
         mCommandQueue = commandQueue;
         mBypassController = keyguardBypassController;
         mStatusBarStateController = statusBarStateController;
         mDumpManager = dumpManager;
         mFooterActionsController = footerActionsController;
         mFooterActionsViewModelFactory = footerActionsViewModelFactory;
-        mFooterActionsViewBinder = footerActionsViewBinder;
         mListeningAndVisibilityLifecycleOwner = new ListeningAndVisibilityLifecycleOwner();
         if (SceneContainerFlag.isEnabled()) {
             mStatusBarState = StatusBarState.SHADE;
@@ -294,43 +288,9 @@
     }
 
     private void bindFooterActionsView(View root) {
-        LinearLayout footerActionsView = root.findViewById(R.id.qs_footer_actions);
-
-        if (!mFeatureFlags.isEnabled(Flags.COMPOSE_QS_FOOTER_ACTIONS)) {
-            Log.d(TAG, "Binding the View implementation of the QS footer actions");
-            mFooterActionsView = footerActionsView;
-            mFooterActionsViewBinder.bind(footerActionsView, mQSFooterActionsViewModel,
-                    mListeningAndVisibilityLifecycleOwner);
-            return;
-        }
-
-        // Compose is available, so let's use the Compose implementation of the footer actions.
-        Log.d(TAG, "Binding the Compose implementation of the QS footer actions");
-        View composeView = QSUtils.createFooterActionsView(root.getContext(),
+        mFooterActionsView = root.findViewById(R.id.qs_footer_actions);
+        QSUtils.setFooterActionsViewContent(mFooterActionsView,
                 mQSFooterActionsViewModel, mListeningAndVisibilityLifecycleOwner);
-        mFooterActionsView = composeView;
-
-        // The id R.id.qs_footer_actions is used by QSContainerImpl to set the horizontal margin
-        // to all views except for qs_footer_actions, so we set it to the Compose view.
-        composeView.setId(R.id.qs_footer_actions);
-
-        // Set this tag so that QSContainerImpl does not add horizontal paddings to this Compose
-        // implementation of the footer actions. They will be set in Compose instead so that the
-        // background fills the full screen width.
-        composeView.setTag(R.id.tag_compose_qs_footer_actions, true);
-
-        // Set the same elevation as the View implementation, otherwise the footer actions will be
-        // drawn below the scroll view with QS grid and clicks won't get through on small devices
-        // where there isn't enough vertical space to show all the tiles and the footer actions.
-        composeView.setElevation(
-                composeView.getContext().getResources().getDimension(R.dimen.qs_panel_elevation));
-
-        // Replace the View by the Compose provided one.
-        ViewGroup parent = (ViewGroup) footerActionsView.getParent();
-        ViewGroup.LayoutParams layoutParams = footerActionsView.getLayoutParams();
-        int index = parent.indexOfChild(footerActionsView);
-        parent.removeViewAt(index);
-        parent.addView(composeView, index, layoutParams);
     }
 
     @Override
@@ -662,6 +622,12 @@
     }
 
     @Override
+    public void setShouldUpdateSquishinessOnMedia(boolean shouldUpdate) {
+        if (DEBUG) Log.d(TAG, "setShouldUpdateSquishinessOnMedia " + shouldUpdate);
+        mShouldUpdateMediaSquishiness = shouldUpdate;
+    }
+
+    @Override
     public void setQsExpansion(float expansion, float panelExpansionFraction,
             float proposedTranslation, float squishinessFraction) {
         float headerTranslation = mTransitioningToFullShade ? 0 : proposedTranslation;
@@ -740,9 +706,11 @@
         if (mQSAnimator != null) {
             mQSAnimator.setPosition(expansion);
         }
-        if (!mInSplitShade
+        if (!mShouldUpdateMediaSquishiness
+                && (!mInSplitShade
                 || mStatusBarStateController.getState() == KEYGUARD
-                || mStatusBarStateController.getState() == SHADE_LOCKED) {
+                || mStatusBarStateController.getState() == SHADE_LOCKED)
+        ) {
             // At beginning, state is 0 and will apply wrong squishiness to MediaHost in lockscreen
             // and media player expect no change by squishiness in lock screen shade. Don't bother
             // squishing mQsMediaHost when not in split shade to prevent problems with stale state.
@@ -1038,6 +1006,7 @@
         indentingPw.println("mTransitioningToFullShade: " + mTransitioningToFullShade);
         indentingPw.println("mLockscreenToShadeProgress: " + mLockscreenToShadeProgress);
         indentingPw.println("mOverScrolling: " + mOverScrolling);
+        indentingPw.println("mShouldUpdateMediaSquishiness: " + mShouldUpdateMediaSquishiness);
         indentingPw.println("isCustomizing: " + mQSCustomizerController.isCustomizing());
         View view = getView();
         if (view != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 00757b7..9c8c17b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -42,6 +42,7 @@
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.settings.brightness.BrightnessSliderController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
@@ -441,7 +442,7 @@
     }
 
     private boolean needsDynamicRowsAndColumns() {
-        return true;
+        return !SceneContainerFlag.isEnabled();
     }
 
     private void switchAllContentToParent(ViewGroup parent, QSTileLayout newLayout) {
@@ -634,8 +635,7 @@
             switchAllContentToParent(newParent, mTileLayout);
             reAttachMediaHost(mediaHostView, horizontal);
             if (needsDynamicRowsAndColumns()) {
-                mTileLayout.setMinRows(horizontal ? 2 : 1);
-                mTileLayout.setMaxColumns(horizontal ? 2 : 4);
+                setColumnRowLayout(horizontal);
             }
             updateMargins(mediaHostView);
             if (mHorizontalLinearLayout != null) {
@@ -644,6 +644,11 @@
         }
     }
 
+    void setColumnRowLayout(boolean withMedia) {
+        mTileLayout.setMinRows(withMedia ? 2 : 1);
+        mTileLayout.setMaxColumns(withMedia ? 2 : 4);
+    }
+
     private void updateMargins(ViewGroup mediaHostView) {
         updateMediaHostContentMargins(mediaHostView);
         updateHorizontalLinearLayoutMargins();
@@ -736,6 +741,8 @@
             return false;
         }
 
+        int getMinRows();
+
         /**
          * Sets the max number of columns to show
          *
@@ -747,6 +754,8 @@
             return false;
         }
 
+        int getMaxColumns();
+
         /**
          * Sets the expansion value and proposedTranslation to panel.
          */
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index e24caf1..f76183e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -30,6 +30,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.haptics.qs.QSLongPressEffect;
+import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor;
 import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
 import com.android.systemui.media.controls.ui.view.MediaHost;
 import com.android.systemui.media.controls.ui.view.MediaHostState;
@@ -46,10 +47,13 @@
 import com.android.systemui.statusbar.policy.SplitShadeStateController;
 import com.android.systemui.tuner.TunerService;
 
+import kotlinx.coroutines.flow.StateFlow;
+
 import javax.inject.Inject;
 import javax.inject.Named;
 import javax.inject.Provider;
 
+
 /**
  * Controller for {@link QSPanel}.
  */
@@ -72,6 +76,8 @@
     private final BrightnessSliderController.Factory mBrightnessSliderControllerFactory;
     private final BrightnessController.Factory mBrightnessControllerFactory;
 
+    protected final MediaCarouselInteractor mMediaCarouselInteractor;
+
     private View.OnTouchListener mTileLayoutTouchListener = new View.OnTouchListener() {
         @Override
         public boolean onTouch(View v, MotionEvent event) {
@@ -94,7 +100,8 @@
             FalsingManager falsingManager,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             SplitShadeStateController splitShadeStateController,
-            Provider<QSLongPressEffect> longPRessEffectProvider) {
+            Provider<QSLongPressEffect> longPRessEffectProvider,
+            MediaCarouselInteractor mediaCarouselInteractor) {
         super(view, qsHost, qsCustomizerController, usingMediaPlayer, mediaHost,
                 metricsLogger, uiEventLogger, qsLogger, dumpManager, splitShadeStateController,
                 longPRessEffectProvider);
@@ -113,6 +120,7 @@
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
         mLastDensity = view.getResources().getConfiguration().densityDpi;
         mSceneContainerEnabled = SceneContainerFlag.isEnabled();
+        mMediaCarouselInteractor = mediaCarouselInteractor;
     }
 
     @Override
@@ -126,6 +134,11 @@
     }
 
     @Override
+    StateFlow<Boolean> getMediaVisibleFlow() {
+        return mMediaCarouselInteractor.getHasAnyMediaOrRecommendation();
+    }
+
+    @Override
     protected void onViewAttached() {
         super.onViewAttached();
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 583cfb9..3b5cc61 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -41,13 +41,17 @@
 import com.android.systemui.qs.external.CustomTile;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileViewImpl;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.statusbar.policy.SplitShadeStateController;
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.animation.DisappearParameters;
+import com.android.systemui.util.kotlin.JavaAdapterKt;
 
 import kotlin.Unit;
 import kotlin.jvm.functions.Function1;
 
+import kotlinx.coroutines.flow.StateFlow;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -58,6 +62,7 @@
 
 import javax.inject.Provider;
 
+
 /**
  * Controller for QSPanel views.
  *
@@ -93,6 +98,15 @@
 
     private final Provider<QSLongPressEffect> mLongPressEffectProvider;
 
+    private boolean mDestroyed = false;
+
+    private boolean mMediaVisibleFromInteractor;
+
+    private final Consumer<Boolean> mMediaOrRecommendationVisibleConsumer = mediaVisible -> {
+        mMediaVisibleFromInteractor = mediaVisible;
+        setLayoutForMediaInScene();
+    };
+
     @VisibleForTesting
     protected final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
             new QSPanel.OnConfigurationChangedListener() {
@@ -115,7 +129,11 @@
                         /* newScreenLayout= */ mLastScreenLayout,
                         /* containerName= */ mView.getDumpableTag());
 
-                    switchTileLayoutIfNeeded();
+                    if (SceneContainerFlag.isEnabled()) {
+                        setLayoutForMediaInScene();
+                    } else {
+                        switchTileLayoutIfNeeded();
+                    }
                     onConfigurationChanged();
                     if (previousSplitShadeState != mShouldUseSplitNotificationShade) {
                         onSplitShadeChanged(mShouldUseSplitNotificationShade);
@@ -173,6 +191,9 @@
         mView.initialize(mQSLogger, mUsingMediaPlayer);
         mQSLogger.logAllTilesChangeListening(mView.isListening(), mView.getDumpableTag(), "");
         mHost.addCallback(mQSHostCallback);
+        if (SceneContainerFlag.isEnabled()) {
+            registerForMediaInteractorChanges();
+        }
     }
 
     /**
@@ -192,7 +213,7 @@
         // will remove the attach listener. We don't need to do that, because once this object is
         // detached from the graph, it will be gc.
         mHost.removeCallback(mQSHostCallback);
-
+        mDestroyed = true;
         for (TileRecord record : mRecords) {
             record.tile.removeCallback(record.callback);
             mView.removeTile(record);
@@ -207,17 +228,32 @@
             mQsTileRevealController.setExpansion(mRevealExpansion);
         }
 
-        mMediaHost.addVisibilityChangeListener(mMediaHostVisibilityListener);
+        if (!SceneContainerFlag.isEnabled()) {
+            mMediaHost.addVisibilityChangeListener(mMediaHostVisibilityListener);
+        }
         mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
         setTiles();
         mLastOrientation = getResources().getConfiguration().orientation;
         mLastScreenLayout = getResources().getConfiguration().screenLayout;
         mQSLogger.logOnViewAttached(mLastOrientation, mView.getDumpableTag());
+        if (SceneContainerFlag.isEnabled()) {
+            setLayoutForMediaInScene();
+        }
         switchTileLayout(true);
 
         mDumpManager.registerDumpable(mView.getDumpableTag(), this);
     }
 
+    private void registerForMediaInteractorChanges() {
+        JavaAdapterKt.collectFlow(
+                mView,
+                getMediaVisibleFlow(),
+                mMediaOrRecommendationVisibleConsumer
+        );
+    }
+
+    abstract StateFlow<Boolean> getMediaVisibleFlow();
+
     @Override
     protected void onViewDetached() {
         mQSLogger.logOnViewDetached(mLastOrientation, mView.getDumpableTag());
@@ -242,6 +278,7 @@
 
     /** */
     public void setTiles(Collection<QSTile> tiles, boolean collapsedView) {
+        if (mDestroyed) return;
         // TODO(b/168904199): move this logic into QSPanelController.
         if (!collapsedView && mQsTileRevealController != null) {
             mQsTileRevealController.updateRevealedTiles(tiles);
@@ -433,6 +470,11 @@
         return false;
     }
 
+    void setLayoutForMediaInScene() {
+        boolean withMedia = shouldUseHorizontalInScene();
+        mView.setColumnRowLayout(withMedia);
+    }
+
     /**
      * Update the way the media disappears based on if we're using the horizontal layout
      */
@@ -473,6 +515,16 @@
                 == Configuration.SCREENLAYOUT_LONG_YES;
     }
 
+    boolean shouldUseHorizontalInScene() {
+        if (mShouldUseSplitNotificationShade) {
+            return false;
+        }
+        return mMediaVisibleFromInteractor
+                && mLastOrientation == Configuration.ORIENTATION_LANDSCAPE
+                && (mLastScreenLayout & Configuration.SCREENLAYOUT_LONG_MASK)
+                == Configuration.SCREENLAYOUT_LONG_YES;
+    }
+
     private void logTiles() {
         for (int i = 0; i < mRecords.size(); i++) {
             QSTile tile = mRecords.get(i).tile;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 828d6ed..03c2aa6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -108,7 +108,7 @@
     private AutoTileManager mAutoTiles;
     private final ArrayList<QSFactory> mQsFactories = new ArrayList<>();
     private int mCurrentUser;
-    private final ShadeController mShadeController;
+    private final Lazy<ShadeController> mShadeControllerProvider;
     private Context mUserContext;
     private UserTracker mUserTracker;
     private SecureSettings mSecureSettings;
@@ -130,7 +130,7 @@
             PluginManager pluginManager,
             TunerService tunerService,
             Provider<AutoTileManager> autoTiles,
-            ShadeController shadeController,
+            Lazy<ShadeController> shadeControllerProvider,
             QSLogger qsLogger,
             UserTracker userTracker,
             SecureSettings secureSettings,
@@ -149,7 +149,7 @@
         mUserFileManager = userFileManager;
         mFeatureFlags = featureFlags;
 
-        mShadeController = shadeController;
+        mShadeControllerProvider = shadeControllerProvider;
 
         if (featureFlags.getTilesEnabled()) {
             mQsFactories.add(newQsTileFactoryProvider.get());
@@ -216,17 +216,17 @@
 
     @Override
     public void collapsePanels() {
-        mShadeController.postAnimateCollapseShade();
+        mShadeControllerProvider.get().postAnimateCollapseShade();
     }
 
     @Override
     public void forceCollapsePanels() {
-        mShadeController.postAnimateForceCollapseShade();
+        mShadeControllerProvider.get().postAnimateForceCollapseShade();
     }
 
     @Override
     public void openPanels() {
-        mShadeController.postAnimateExpandQs();
+        mShadeControllerProvider.get().postAnimateExpandQs();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt b/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt
index 15c3f27..5482e6d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSUtils.kt
@@ -1,10 +1,9 @@
 package com.android.systemui.qs
 
 import android.content.Context
-import android.view.View
+import androidx.compose.ui.platform.ComposeView
 import androidx.lifecycle.LifecycleOwner
 import com.android.compose.theme.PlatformTheme
-import com.android.compose.ui.platform.DensityAwareComposeView
 import com.android.internal.policy.SystemBarUtils
 import com.android.systemui.qs.footer.ui.compose.FooterActions
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
@@ -29,13 +28,11 @@
     }
 
     @JvmStatic
-    fun createFooterActionsView(
-        context: Context,
+    fun setFooterActionsViewContent(
+        view: ComposeView,
         viewModel: FooterActionsViewModel,
         qsVisibilityLifecycleOwner: LifecycleOwner,
-    ): View {
-        return DensityAwareComposeView(context).apply {
-            setContent { PlatformTheme { FooterActions(viewModel, qsVisibilityLifecycleOwner) } }
-        }
+    ) {
+        view.setContent { PlatformTheme { FooterActions(viewModel, qsVisibilityLifecycleOwner) } }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 6cda740..f207b1d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -26,6 +26,7 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.haptics.qs.QSLongPressEffect;
+import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor;
 import com.android.systemui.media.controls.ui.controller.MediaHierarchyManager;
 import com.android.systemui.media.controls.ui.view.MediaHost;
 import com.android.systemui.plugins.qs.QSTile;
@@ -36,6 +37,8 @@
 import com.android.systemui.statusbar.policy.SplitShadeStateController;
 import com.android.systemui.util.leak.RotationUtils;
 
+import kotlinx.coroutines.flow.StateFlow;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -43,12 +46,15 @@
 import javax.inject.Named;
 import javax.inject.Provider;
 
+
 /** Controller for {@link QuickQSPanel}. */
 @QSScope
 public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> {
 
     private final Provider<Boolean> mUsingCollapsedLandscapeMediaProvider;
 
+    private final MediaCarouselInteractor mMediaCarouselInteractor;
+
     @Inject
     QuickQSPanelController(QuickQSPanel view, QSHost qsHost,
             QSCustomizerController qsCustomizerController,
@@ -58,12 +64,14 @@
                     Provider<Boolean> usingCollapsedLandscapeMediaProvider,
             MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
             DumpManager dumpManager, SplitShadeStateController splitShadeStateController,
-            Provider<QSLongPressEffect> longPressEffectProvider
+            Provider<QSLongPressEffect> longPressEffectProvider,
+            MediaCarouselInteractor mediaCarouselInteractor
     ) {
         super(view, qsHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
                 uiEventLogger, qsLogger, dumpManager, splitShadeStateController,
                 longPressEffectProvider);
         mUsingCollapsedLandscapeMediaProvider = usingCollapsedLandscapeMediaProvider;
+        mMediaCarouselInteractor = mediaCarouselInteractor;
     }
 
     @Override
@@ -74,6 +82,11 @@
         mMediaHost.init(MediaHierarchyManager.LOCATION_QQS);
     }
 
+    @Override
+    StateFlow<Boolean> getMediaVisibleFlow() {
+        return mMediaCarouselInteractor.getHasActiveMediaOrRecommendation();
+    }
+
     private void updateMediaExpansion() {
         int rotation = getRotation();
         boolean isLandscape = rotation == RotationUtils.ROTATION_LANDSCAPE
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsControllerImpl.java
index 4fc6609..846d63f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsControllerImpl.java
@@ -77,8 +77,8 @@
             public void onUserChanged(int newUser, Context userContext) {
                 synchronized (mListeners) {
                     if (mListeners.size() > 0) {
-                        mSecureSettings.unregisterContentObserver(mContentObserver);
-                        mSecureSettings.registerContentObserverForUser(
+                        mSecureSettings.unregisterContentObserverSync(mContentObserver);
+                        mSecureSettings.registerContentObserverForUserSync(
                                 Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
                                 false, mContentObserver, newUser);
                     }
@@ -94,7 +94,7 @@
             if (!mListeners.contains(listener)) {
                 mListeners.add(listener);
                 if (mListeners.size() == 1) {
-                    mSecureSettings.registerContentObserverForUser(
+                    mSecureSettings.registerContentObserverForUserSync(
                             Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
                             false, mContentObserver, mUserTracker.getUserId());
                 }
@@ -106,7 +106,7 @@
     public void removeCallback(@androidx.annotation.NonNull Listener listener) {
         synchronized (mListeners) {
             if (mListeners.remove(listener) && mListeners.size() == 0) {
-                mSecureSettings.unregisterContentObserver(mContentObserver);
+                mSecureSettings.unregisterContentObserverSync(mContentObserver);
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SettingObserver.java b/packages/SystemUI/src/com/android/systemui/qs/SettingObserver.java
index eb11568..6092348 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SettingObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/SettingObserver.java
@@ -74,10 +74,10 @@
         mListening = listening;
         if (listening) {
             mObservedValue = getValueFromProvider();
-            mSettingsProxy.registerContentObserver(
+            mSettingsProxy.registerContentObserverSync(
                     mSettingsProxy.getUriFor(mSettingName), false, this);
         } else {
-            mSettingsProxy.unregisterContentObserver(this);
+            mSettingsProxy.unregisterContentObserverSync(this);
             mObservedValue = mDefaultValue;
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
index dcb9288..ef44e5f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java
@@ -12,6 +12,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
 
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.FontSizeUtils;
@@ -97,12 +98,24 @@
         return false;
     }
 
+    @VisibleForTesting
+    @Override
+    public int getMinRows() {
+        return mMinRows;
+    }
+
     @Override
     public boolean setMaxColumns(int maxColumns) {
         mMaxColumns = maxColumns;
         return updateColumns();
     }
 
+    @VisibleForTesting
+    @Override
+    public int getMaxColumns() {
+        return mMaxColumns;
+    }
+
     public void addTile(TileRecord tile) {
         mRecords.add(tile);
         tile.tile.setListening(this, mListening);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/UserSettingObserver.java b/packages/SystemUI/src/com/android/systemui/qs/UserSettingObserver.java
index 539c2d6..1b34c33 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/UserSettingObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/UserSettingObserver.java
@@ -76,10 +76,10 @@
         mListening = listening;
         if (listening) {
             mObservedValue = getValueFromProvider();
-            mSettingsProxy.registerContentObserverForUser(
+            mSettingsProxy.registerContentObserverForUserSync(
                     mSettingsProxy.getUriFor(mSettingName), false, this, mUserId);
         } else {
-            mSettingsProxy.unregisterContentObserver(this);
+            mSettingsProxy.unregisterContentObserverSync(this);
             mObservedValue = mDefaultValue;
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
deleted file mode 100644
index 0995dd4..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
+++ /dev/null
@@ -1,329 +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.qs.footer.ui.binder
-
-import android.content.Context
-import android.graphics.PorterDuff
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.ImageView
-import android.widget.LinearLayout
-import android.widget.TextView
-import androidx.core.view.isVisible
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.animation.Expandable
-import com.android.systemui.common.ui.binder.IconViewBinder
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.lifecycle.repeatWhenAttached
-import com.android.systemui.people.ui.view.PeopleViewBinder.bind
-import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsButtonViewModel
-import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsForegroundServicesButtonViewModel
-import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsSecurityButtonViewModel
-import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
-import com.android.systemui.res.R
-import javax.inject.Inject
-import kotlin.math.roundToInt
-import kotlinx.coroutines.launch
-
-/** A ViewBinder for [FooterActionsViewBinder]. */
-@SysUISingleton
-class FooterActionsViewBinder @Inject constructor() {
-    /** Create a view that can later be [bound][bind] to a [FooterActionsViewModel]. */
-    fun create(context: Context): LinearLayout {
-        return LayoutInflater.from(context).inflate(R.layout.footer_actions, /* root= */ null)
-            as LinearLayout
-    }
-
-    /** Bind [view] to [viewModel]. */
-    fun bind(
-        view: LinearLayout,
-        viewModel: FooterActionsViewModel,
-        qsVisibilityLifecycleOwner: LifecycleOwner,
-    ) {
-        view.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
-
-        // Add the views used by this new implementation.
-        val context = view.context
-        val inflater = LayoutInflater.from(context)
-
-        val securityHolder = TextButtonViewHolder.createAndAdd(inflater, view)
-        val foregroundServicesWithTextHolder = TextButtonViewHolder.createAndAdd(inflater, view)
-        val foregroundServicesWithNumberHolder = NumberButtonViewHolder.createAndAdd(inflater, view)
-        val userSwitcherHolder = IconButtonViewHolder.createAndAdd(inflater, view, isLast = false)
-        val settingsHolder =
-            IconButtonViewHolder.createAndAdd(inflater, view, isLast = viewModel.power == null)
-
-        // Bind the static power and settings buttons.
-        bindButton(settingsHolder, viewModel.settings)
-
-        if (viewModel.power != null) {
-            val powerHolder = IconButtonViewHolder.createAndAdd(inflater, view, isLast = true)
-            bindButton(powerHolder, viewModel.power)
-        }
-
-        // There are 2 lifecycle scopes we are using here:
-        //   1) The scope created by [repeatWhenAttached] when [view] is attached, and destroyed
-        //      when the [view] is detached. We use this as the parent scope for all our [viewModel]
-        //      state collection, given that we don't want to do any work when [view] is detached.
-        //   2) The scope owned by [lifecycleOwner], which should be RESUMED only when Quick
-        //      Settings are visible. We use this to make sure we collect UI state only when the
-        //      View is visible.
-        //
-        // Given that we start our collection when the Quick Settings become visible, which happens
-        // every time the user swipes down the shade, we remember our previous UI state already
-        // bound to the UI to avoid binding the same values over and over for nothing.
-
-        // TODO(b/242040009): Look into using only a single scope.
-
-        var previousSecurity: FooterActionsSecurityButtonViewModel? = null
-        var previousForegroundServices: FooterActionsForegroundServicesButtonViewModel? = null
-        var previousUserSwitcher: FooterActionsButtonViewModel? = null
-
-        // Listen for ViewModel updates when the View is attached.
-        view.repeatWhenAttached {
-            val attachedScope = this.lifecycleScope
-
-            attachedScope.launch {
-                // Listen for dialog requests as soon as we are attached, even when not visible.
-                // TODO(b/242040009): Should this move somewhere else?
-                launch { viewModel.observeDeviceMonitoringDialogRequests(view.context) }
-
-                // Make sure we set the correct alphas even when QS are not currently shown.
-                launch { viewModel.alpha.collect { view.alpha = it } }
-                launch {
-                    viewModel.backgroundAlpha.collect {
-                        view.background?.alpha = (it * 255).roundToInt()
-                    }
-                }
-            }
-
-            // Listen for model changes only when QS are visible.
-            qsVisibilityLifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) {
-                // Security.
-                launch {
-                    viewModel.security.collect { security ->
-                        if (previousSecurity != security) {
-                            bindSecurity(view.context, securityHolder, security)
-                            previousSecurity = security
-                        }
-                    }
-                }
-
-                // Foreground services.
-                launch {
-                    viewModel.foregroundServices.collect { foregroundServices ->
-                        if (previousForegroundServices != foregroundServices) {
-                            bindForegroundService(
-                                foregroundServicesWithNumberHolder,
-                                foregroundServicesWithTextHolder,
-                                foregroundServices,
-                            )
-                            previousForegroundServices = foregroundServices
-                        }
-                    }
-                }
-
-                // User switcher.
-                launch {
-                    viewModel.userSwitcher.collect { userSwitcher ->
-                        if (previousUserSwitcher != userSwitcher) {
-                            bindButton(userSwitcherHolder, userSwitcher)
-                            previousUserSwitcher = userSwitcher
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    private fun bindSecurity(
-        quickSettingsContext: Context,
-        securityHolder: TextButtonViewHolder,
-        security: FooterActionsSecurityButtonViewModel?,
-    ) {
-        val securityView = securityHolder.view
-        securityView.isVisible = security != null
-        if (security == null) {
-            return
-        }
-
-        // Make sure that the chevron is visible and that the button is clickable if there is a
-        // listener.
-        val chevron = securityHolder.chevron
-        val onClick = security.onClick
-        if (onClick != null) {
-            securityView.isClickable = true
-            securityView.setOnClickListener {
-                onClick(quickSettingsContext, Expandable.fromView(securityView))
-            }
-            chevron.isVisible = true
-        } else {
-            securityView.isClickable = false
-            securityView.setOnClickListener(null)
-            chevron.isVisible = false
-        }
-
-        securityHolder.text.text = security.text
-        securityHolder.newDot.isVisible = false
-        IconViewBinder.bind(security.icon, securityHolder.icon)
-    }
-
-    private fun bindForegroundService(
-        foregroundServicesWithNumberHolder: NumberButtonViewHolder,
-        foregroundServicesWithTextHolder: TextButtonViewHolder,
-        foregroundServices: FooterActionsForegroundServicesButtonViewModel?,
-    ) {
-        val foregroundServicesWithNumberView = foregroundServicesWithNumberHolder.view
-        val foregroundServicesWithTextView = foregroundServicesWithTextHolder.view
-        if (foregroundServices == null) {
-            foregroundServicesWithNumberView.isVisible = false
-            foregroundServicesWithTextView.isVisible = false
-            return
-        }
-
-        val foregroundServicesCount = foregroundServices.foregroundServicesCount
-        if (foregroundServices.displayText) {
-            // Button with text, icon and chevron.
-            foregroundServicesWithNumberView.isVisible = false
-
-            foregroundServicesWithTextView.isVisible = true
-            foregroundServicesWithTextView.setOnClickListener {
-                foregroundServices.onClick(Expandable.fromView(foregroundServicesWithTextView))
-            }
-            foregroundServicesWithTextHolder.text.text = foregroundServices.text
-            foregroundServicesWithTextHolder.newDot.isVisible = foregroundServices.hasNewChanges
-        } else {
-            // Small button with the number only.
-            foregroundServicesWithTextView.isVisible = false
-
-            foregroundServicesWithNumberView.isVisible = true
-            foregroundServicesWithNumberView.setOnClickListener {
-                foregroundServices.onClick(Expandable.fromView(foregroundServicesWithNumberView))
-            }
-            foregroundServicesWithNumberHolder.number.text = foregroundServicesCount.toString()
-            foregroundServicesWithNumberHolder.number.contentDescription = foregroundServices.text
-            foregroundServicesWithNumberHolder.newDot.isVisible = foregroundServices.hasNewChanges
-        }
-    }
-
-    private fun bindButton(button: IconButtonViewHolder, model: FooterActionsButtonViewModel?) {
-        val buttonView = button.view
-        buttonView.id = model?.id ?: View.NO_ID
-        buttonView.isVisible = model != null
-        if (model == null) {
-            return
-        }
-
-        val backgroundResource =
-            when (model.backgroundColor) {
-                R.attr.shadeInactive -> R.drawable.qs_footer_action_circle
-                R.attr.shadeActive -> R.drawable.qs_footer_action_circle_color
-                else -> error("Unsupported icon background resource ${model.backgroundColor}")
-            }
-        buttonView.setBackgroundResource(backgroundResource)
-        buttonView.setOnClickListener { model.onClick(Expandable.fromView(buttonView)) }
-
-        val icon = model.icon
-        val iconView = button.icon
-
-        IconViewBinder.bind(icon, iconView)
-        if (model.iconTint != null) {
-            iconView.setColorFilter(model.iconTint, PorterDuff.Mode.SRC_IN)
-        } else {
-            iconView.clearColorFilter()
-        }
-    }
-}
-
-private class TextButtonViewHolder(val view: View) {
-    val icon = view.requireViewById<ImageView>(R.id.icon)
-    val text = view.requireViewById<TextView>(R.id.text)
-    val newDot = view.requireViewById<ImageView>(R.id.new_dot)
-    val chevron = view.requireViewById<ImageView>(R.id.chevron_icon)
-
-    companion object {
-        fun createAndAdd(inflater: LayoutInflater, root: ViewGroup): TextButtonViewHolder {
-            val view =
-                inflater.inflate(
-                    R.layout.footer_actions_text_button,
-                    /* root= */ root,
-                    /* attachToRoot= */ false,
-                )
-            root.addView(view)
-            return TextButtonViewHolder(view)
-        }
-    }
-}
-
-private class NumberButtonViewHolder(val view: View) {
-    val number = view.requireViewById<TextView>(R.id.number)
-    val newDot = view.requireViewById<ImageView>(R.id.new_dot)
-
-    companion object {
-        fun createAndAdd(inflater: LayoutInflater, root: ViewGroup): NumberButtonViewHolder {
-            val view =
-                inflater.inflate(
-                    R.layout.footer_actions_number_button,
-                    /* root= */ root,
-                    /* attachToRoot= */ false,
-                )
-            root.addView(view)
-            return NumberButtonViewHolder(view)
-        }
-    }
-}
-
-private class IconButtonViewHolder(val view: View) {
-    val icon = view.requireViewById<ImageView>(R.id.icon)
-
-    companion object {
-        fun createAndAdd(
-            inflater: LayoutInflater,
-            root: ViewGroup,
-            isLast: Boolean,
-        ): IconButtonViewHolder {
-            val view =
-                inflater.inflate(
-                    R.layout.footer_actions_icon_button,
-                    /* root= */ root,
-                    /* attachToRoot= */ false,
-                )
-
-            // All buttons have a background with an inset of qs_footer_action_inset, so the last
-            // button must have a negative inset of -qs_footer_action_inset to compensate and be
-            // aligned with its parent.
-            val marginEnd =
-                if (isLast) {
-                    -view.context.resources.getDimensionPixelSize(R.dimen.qs_footer_action_inset)
-                } else {
-                    0
-                }
-
-            val size =
-                view.context.resources.getDimensionPixelSize(R.dimen.qs_footer_action_button_size)
-            root.addView(
-                view,
-                LinearLayout.LayoutParams(size, size).apply { this.marginEnd = marginEnd },
-            )
-            return IconButtonViewHolder(view)
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepository.kt
deleted file mode 100644
index 686e5f4..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepository.kt
+++ /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 com.android.systemui.qs.panels.data.repository
-
-import com.android.systemui.dagger.SysUISingleton
-import javax.inject.Inject
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-
-/** Repository for whether to show the labels of icon tiles. */
-@SysUISingleton
-class IconLabelVisibilityRepository @Inject constructor() {
-    // TODO(b/341735914): Persist and back up showLabels
-    private val _showLabels = MutableStateFlow(false)
-    val showLabels: StateFlow<Boolean> = _showLabels.asStateFlow()
-
-    fun setShowLabels(showLabels: Boolean) {
-        _showLabels.value = showLabels
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconTilesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconTilesRepository.kt
index e581bfc..095bdf2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconTilesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconTilesRepository.kt
@@ -19,38 +19,26 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import javax.inject.Inject
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
 
-/** Repository for retrieving the list of [TileSpec] to be displayed as icons. */
+/** Repository for checking if a tile should be displayed as an icon. */
 interface IconTilesRepository {
-    val iconTilesSpecs: StateFlow<Set<TileSpec>>
+    fun isIconTile(spec: TileSpec): Boolean
 }
 
 @SysUISingleton
 class IconTilesRepositoryImpl @Inject constructor() : IconTilesRepository {
 
-    private val _iconTilesSpecs =
-        MutableStateFlow(
-            setOf(
-                TileSpec.create("airplane"),
-                TileSpec.create("battery"),
-                TileSpec.create("cameratoggle"),
-                TileSpec.create("cast"),
-                TileSpec.create("color_correction"),
-                TileSpec.create("inversion"),
-                TileSpec.create("saver"),
-                TileSpec.create("dnd"),
-                TileSpec.create("flashlight"),
-                TileSpec.create("location"),
-                TileSpec.create("mictoggle"),
-                TileSpec.create("nfc"),
-                TileSpec.create("night"),
-                TileSpec.create("rotation")
-            )
-        )
+    override fun isIconTile(spec: TileSpec): Boolean {
+        return !LARGE_TILES.contains(spec)
+    }
 
-    /** Set of toggleable tiles that are suitable for being shown as an icon. */
-    override val iconTilesSpecs: StateFlow<Set<TileSpec>> = _iconTilesSpecs.asStateFlow()
+    companion object {
+        private val LARGE_TILES =
+            setOf(
+                TileSpec.create("internet"),
+                TileSpec.create("bt"),
+                TileSpec.create("dnd"),
+                TileSpec.create("cast"),
+            )
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepository.kt
new file mode 100644
index 0000000..f3e5b8f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepository.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.data.repository
+
+import android.content.Context
+import android.content.SharedPreferences
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.user.data.repository.UserRepository
+import com.android.systemui.util.kotlin.SharedPreferencesExt.observe
+import com.android.systemui.util.kotlin.emitOnStart
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
+
+/** Repository for QS user preferences. */
+@OptIn(ExperimentalCoroutinesApi::class)
+@SysUISingleton
+class QSPreferencesRepository
+@Inject
+constructor(
+    private val userFileManager: UserFileManager,
+    private val userRepository: UserRepository,
+    @Background private val backgroundDispatcher: CoroutineDispatcher,
+) {
+    /** Whether to show the labels on icon tiles for the current user. */
+    val showLabels: Flow<Boolean> =
+        userRepository.selectedUserInfo
+            .flatMapLatest { userInfo ->
+                val prefs = getSharedPrefs(userInfo.id)
+                prefs.observe().emitOnStart().map { prefs.getBoolean(ICON_LABELS_KEY, false) }
+            }
+            .flowOn(backgroundDispatcher)
+
+    /** Sets for the current user whether to show the labels on icon tiles. */
+    fun setShowLabels(showLabels: Boolean) {
+        with(getSharedPrefs(userRepository.getSelectedUserInfo().id)) {
+            edit().putBoolean(ICON_LABELS_KEY, showLabels).apply()
+        }
+    }
+
+    private fun getSharedPrefs(userId: Int): SharedPreferences {
+        return userFileManager.getSharedPreferences(
+            FILE_NAME,
+            Context.MODE_PRIVATE,
+            userId,
+        )
+    }
+
+    companion object {
+        private const val ICON_LABELS_KEY = "show_icon_labels"
+        const val FILE_NAME = "quick_settings_prefs"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractor.kt
index a871531..6a899b0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractor.kt
@@ -20,7 +20,6 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel
-import com.android.systemui.qs.panels.data.repository.IconLabelVisibilityRepository
 import com.android.systemui.qs.panels.shared.model.IconLabelVisibilityLog
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -33,17 +32,17 @@
 class IconLabelVisibilityInteractor
 @Inject
 constructor(
-    private val repo: IconLabelVisibilityRepository,
+    private val preferencesInteractor: QSPreferencesInteractor,
     @IconLabelVisibilityLog private val logBuffer: LogBuffer,
     @Application scope: CoroutineScope,
 ) {
     val showLabels: StateFlow<Boolean> =
-        repo.showLabels
+        preferencesInteractor.showLabels
             .onEach { logChange(it) }
-            .stateIn(scope, SharingStarted.WhileSubscribed(), repo.showLabels.value)
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
 
     fun setShowLabels(showLabels: Boolean) {
-        repo.setShowLabels(showLabels)
+        preferencesInteractor.setShowLabels(showLabels)
     }
 
     private fun logChange(showLabels: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
index ccc1c6e..524ea8b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
@@ -20,10 +20,9 @@
 import com.android.systemui.qs.panels.data.repository.IconTilesRepository
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import javax.inject.Inject
-import kotlinx.coroutines.flow.StateFlow
 
 /** Interactor for retrieving the list of [TileSpec] to be displayed as icons. */
 @SysUISingleton
-class IconTilesInteractor @Inject constructor(repo: IconTilesRepository) {
-    val iconTilesSpecs: StateFlow<Set<TileSpec>> = repo.iconTilesSpecs
+class IconTilesInteractor @Inject constructor(private val repo: IconTilesRepository) {
+    fun isIconTile(spec: TileSpec): Boolean = repo.isIconTile(spec)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt
index b437f64..e99c64c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt
@@ -38,14 +38,13 @@
     override fun reconcileTiles(tiles: List<TileSpec>): List<TileSpec> {
         val newTiles: MutableList<TileSpec> = mutableListOf()
         val row = TileRow<TileSpec>(columns = gridSizeInteractor.columns.value)
-        val iconTilesSet = iconTilesInteractor.iconTilesSpecs.value
         val tilesQueue =
             ArrayDeque(
                 tiles.map {
                     SizedTile(
                         it,
                         width =
-                            if (iconTilesSet.contains(it)) {
+                            if (iconTilesInteractor.isIconTile(it)) {
                                 1
                             } else {
                                 2
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSPreferencesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSPreferencesInteractor.kt
new file mode 100644
index 0000000..811be80
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/QSPreferencesInteractor.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.panels.data.repository.QSPreferencesRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+@SysUISingleton
+class QSPreferencesInteractor @Inject constructor(private val repo: QSPreferencesRepository) {
+    val showLabels: Flow<Boolean> = repo.showLabels
+
+    fun setShowLabels(showLabels: Boolean) {
+        repo.setShowLabels(showLabels)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
index 4aeaa7d..2f0fe22 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
@@ -52,15 +52,13 @@
             tiles.forEach { it.startListening(token) }
             onDispose { tiles.forEach { it.stopListening(token) } }
         }
-        val iconTilesSpecs by iconTilesViewModel.iconTilesSpecs.collectAsStateWithLifecycle()
         val columns by gridSizeViewModel.columns.collectAsStateWithLifecycle()
 
         TileLazyGrid(modifier = modifier, columns = GridCells.Fixed(columns)) {
             items(
                 tiles.size,
                 span = { index ->
-                    val iconOnly = iconTilesSpecs.contains(tiles[index].spec)
-                    if (iconOnly) {
+                    if (iconTilesViewModel.isIconTile(tiles[index].spec)) {
                         GridItemSpan(1)
                     } else {
                         GridItemSpan(2)
@@ -69,7 +67,7 @@
             ) { index ->
                 Tile(
                     tile = tiles[index],
-                    iconOnly = iconTilesSpecs.contains(tiles[index].spec),
+                    iconOnly = iconTilesViewModel.isIconTile(tiles[index].spec),
                     modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
                 )
             }
@@ -83,12 +81,11 @@
         onAddTile: (TileSpec, Int) -> Unit,
         onRemoveTile: (TileSpec) -> Unit,
     ) {
-        val iconOnlySpecs by iconTilesViewModel.iconTilesSpecs.collectAsStateWithLifecycle()
         val columns by gridSizeViewModel.columns.collectAsStateWithLifecycle()
 
         DefaultEditTileGrid(
             tiles = tiles,
-            iconOnlySpecs = iconOnlySpecs,
+            isIconOnly = iconTilesViewModel::isIconTile,
             columns = GridCells.Fixed(columns),
             modifier = modifier,
             onAddTile = onAddTile,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt
index 708ef0d..d600767 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt
@@ -38,7 +38,6 @@
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.remember
 import androidx.compose.runtime.rememberUpdatedState
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.dimensionResource
@@ -66,12 +65,11 @@
             tiles.forEach { it.startListening(token) }
             onDispose { tiles.forEach { it.stopListening(token) } }
         }
-        val iconTilesSpecs by viewModel.iconTilesSpecs.collectAsStateWithLifecycle()
         val columns by viewModel.columns.collectAsStateWithLifecycle()
         val showLabels by viewModel.showLabels.collectAsStateWithLifecycle()
         val largeTileHeight = tileHeight()
         val iconTileHeight = tileHeight(showLabels)
-        val (smallTiles, largeTiles) = tiles.partition { iconTilesSpecs.contains(it.spec) }
+        val (smallTiles, largeTiles) = tiles.partition { viewModel.isIconTile(it.spec) }
 
         TileLazyGrid(modifier = modifier, columns = GridCells.Fixed(columns)) {
             // Large tiles
@@ -103,7 +101,6 @@
         onAddTile: (TileSpec, Int) -> Unit,
         onRemoveTile: (TileSpec) -> Unit
     ) {
-        val iconOnlySpecs by viewModel.iconTilesSpecs.collectAsStateWithLifecycle()
         val columns by viewModel.columns.collectAsStateWithLifecycle()
         val showLabels by viewModel.showLabels.collectAsStateWithLifecycle()
 
@@ -111,8 +108,6 @@
         val addTileToEnd: (TileSpec) -> Unit by rememberUpdatedState {
             onAddTile(it, CurrentTilesInteractor.POSITION_AT_END)
         }
-        val isIconOnly: (TileSpec) -> Boolean =
-            remember(iconOnlySpecs) { { tileSpec: TileSpec -> tileSpec in iconOnlySpecs } }
         val largeTileHeight = tileHeight()
         val iconTileHeight = tileHeight(showLabels)
         val tilePadding = dimensionResource(R.dimen.qs_tile_margin_vertical)
@@ -151,7 +146,7 @@
                 iconTileHeight = iconTileHeight,
                 tilePadding = tilePadding,
                 onRemoveTile = onRemoveTile,
-                isIconOnly = isIconOnly,
+                isIconOnly = viewModel::isIconTile,
                 columns = columns,
                 showLabels = showLabels,
             )
@@ -161,7 +156,7 @@
                 iconTileHeight = iconTileHeight,
                 tilePadding = tilePadding,
                 addTileToEnd = addTileToEnd,
-                isIconOnly = isIconOnly,
+                isIconOnly = viewModel::isIconTile,
                 showLabels = showLabels,
                 columns = columns,
             )
@@ -232,7 +227,7 @@
         val largeGridHeight = gridHeight(largeTiles.size, largeTileHeight, columns / 2, tilePadding)
         val smallGridHeight = gridHeight(smallTiles.size, iconTileHeight, columns, tilePadding)
         val largeGridHeightCustom =
-            gridHeight(tilesCustom.size, largeTileHeight, columns / 2, tilePadding)
+            gridHeight(tilesCustom.size, iconTileHeight, columns, tilePadding)
 
         // Add up the height of all three grids and add padding in between
         val gridHeight =
@@ -257,8 +252,14 @@
                 )
                 fillUpRow(nTiles = smallTiles.size, columns = columns)
 
-                // Custom tiles, all large
-                editTiles(tilesCustom, ClickAction.ADD, addTileToEnd, isIconOnly)
+                // Custom tiles, all icons
+                editTiles(
+                    tilesCustom,
+                    ClickAction.ADD,
+                    addTileToEnd,
+                    isIconOnly,
+                    showLabels = showLabels
+                )
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/StretchedGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/StretchedGridLayout.kt
index 70d629f..7f4e0a7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/StretchedGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/StretchedGridLayout.kt
@@ -60,14 +60,13 @@
         // Icon [3 | 4]
         // Large [6 | 8]
         val columns = 12
-        val iconTilesSpecs by iconTilesViewModel.iconTilesSpecs.collectAsStateWithLifecycle()
         val stretchedTiles =
             remember(tiles) {
                 val sizedTiles =
                     tiles.map {
                         SizedTile(
                             it,
-                            if (iconTilesSpecs.contains(it.spec)) {
+                            if (iconTilesViewModel.isIconTile(it.spec)) {
                                 3
                             } else {
                                 6
@@ -81,7 +80,7 @@
             items(stretchedTiles.size, span = { GridItemSpan(stretchedTiles[it].width) }) { index ->
                 Tile(
                     tile = stretchedTiles[index].tile,
-                    iconOnly = iconTilesSpecs.contains(stretchedTiles[index].tile.spec),
+                    iconOnly = iconTilesViewModel.isIconTile(stretchedTiles[index].tile.spec),
                     modifier = Modifier.height(dimensionResource(id = R.dimen.qs_tile_height))
                 )
             }
@@ -95,12 +94,11 @@
         onAddTile: (TileSpec, Int) -> Unit,
         onRemoveTile: (TileSpec) -> Unit
     ) {
-        val iconOnlySpecs by iconTilesViewModel.iconTilesSpecs.collectAsStateWithLifecycle()
         val columns by gridSizeViewModel.columns.collectAsStateWithLifecycle()
 
         DefaultEditTileGrid(
             tiles = tiles,
-            iconOnlySpecs = iconOnlySpecs,
+            isIconOnly = iconTilesViewModel::isIconTile,
             columns = GridCells.Fixed(columns),
             modifier = modifier,
             onAddTile = onAddTile,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
index a6838c0..f776bf0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
@@ -165,7 +165,7 @@
 @Composable
 fun DefaultEditTileGrid(
     tiles: List<EditTileViewModel>,
-    iconOnlySpecs: Set<TileSpec>,
+    isIconOnly: (TileSpec) -> Boolean,
     columns: GridCells,
     modifier: Modifier,
     onAddTile: (TileSpec, Int) -> Unit,
@@ -176,8 +176,6 @@
     val addTileToEnd: (TileSpec) -> Unit by rememberUpdatedState {
         onAddTile(it, CurrentTilesInteractor.POSITION_AT_END)
     }
-    val isIconOnly: (TileSpec) -> Boolean =
-        remember(iconOnlySpecs) { { tileSpec: TileSpec -> tileSpec in iconOnlySpecs } }
 
     TileLazyGrid(modifier = modifier, columns = columns) {
         // These Text are just placeholders to see the different sections. Not final UI.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconLabelVisibilityViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconLabelVisibilityViewModel.kt
index 5d4b8f1..12cbde2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconLabelVisibilityViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconLabelVisibilityViewModel.kt
@@ -30,7 +30,9 @@
 @SysUISingleton
 class IconLabelVisibilityViewModelImpl
 @Inject
-constructor(private val interactor: IconLabelVisibilityInteractor) : IconLabelVisibilityViewModel {
+constructor(
+    private val interactor: IconLabelVisibilityInteractor,
+) : IconLabelVisibilityViewModel {
     override val showLabels: StateFlow<Boolean> = interactor.showLabels
 
     override fun setShowLabels(showLabels: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt
index 9ad00c8..117c85c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/IconTilesViewModel.kt
@@ -20,14 +20,13 @@
 import com.android.systemui.qs.panels.domain.interactor.IconTilesInteractor
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import javax.inject.Inject
-import kotlinx.coroutines.flow.StateFlow
 
 interface IconTilesViewModel {
-    val iconTilesSpecs: StateFlow<Set<TileSpec>>
+    fun isIconTile(spec: TileSpec): Boolean
 }
 
 @SysUISingleton
-class IconTilesViewModelImpl @Inject constructor(interactor: IconTilesInteractor) :
+class IconTilesViewModelImpl @Inject constructor(private val interactor: IconTilesInteractor) :
     IconTilesViewModel {
-    override val iconTilesSpecs = interactor.iconTilesSpecs
+    override fun isIconTile(spec: TileSpec): Boolean = interactor.isIconTile(spec)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
index 214e9f0..24b80b8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/TileSpecRepository.kt
@@ -158,6 +158,9 @@
     override suspend fun prependDefault(
         userId: Int,
     ) {
+        if (retailModeRepository.inRetailMode) {
+            return
+        }
         userTileRepositories.get(userId)?.prependDefault()
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt
index d452241..9fcb0db 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt
@@ -44,9 +44,8 @@
     @Background private val bgDispatcher: CoroutineDispatcher,
 ) {
 
-    private val changeEvents = MutableSharedFlow<ChangeAction>(
-        extraBufferCapacity = CHANGES_BUFFER_SIZE
-    )
+    private val changeEvents =
+        MutableSharedFlow<ChangeAction>(extraBufferCapacity = CHANGES_BUFFER_SIZE)
 
     private lateinit var _autoAdded: StateFlow<Set<TileSpec>>
 
@@ -85,8 +84,8 @@
                                     trySend(Unit)
                                 }
                             }
-                        secureSettings.registerContentObserverForUser(SETTING, observer, userId)
-                        awaitClose { secureSettings.unregisterContentObserver(observer) }
+                        secureSettings.registerContentObserverForUserSync(SETTING, observer, userId)
+                        awaitClose { secureSettings.unregisterContentObserverSync(observer) }
                     }
                     .map { load() }
                     .flowOn(bgDispatcher)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
index 8ad5cb2..1f9570a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
@@ -60,15 +60,21 @@
             _tiles =
                 changeEvents
                     .scan(loadTilesFromSettingsAndParse(userId)) { current, change ->
-                        change.apply(current).also {
-                            if (current != it) {
-                                if (change is RestoreTiles) {
-                                    logger.logTilesRestoredAndReconciled(current, it, userId)
-                                } else {
-                                    logger.logProcessTileChange(change, it, userId)
+                        change
+                            .apply(current)
+                            .also {
+                                if (current != it) {
+                                    if (change is RestoreTiles) {
+                                        logger.logTilesRestoredAndReconciled(current, it, userId)
+                                    } else {
+                                        logger.logProcessTileChange(change, it, userId)
+                                    }
                                 }
                             }
-                        }
+                            // Distinct preserves the order of the elements removing later
+                            // duplicates,
+                            // all tiles should be different
+                            .distinct()
                     }
                     .flowOn(backgroundDispatcher)
                     .stateIn(applicationScope)
@@ -92,8 +98,8 @@
                                     trySend(Unit)
                                 }
                             }
-                        secureSettings.registerContentObserverForUser(SETTING, observer, userId)
-                        awaitClose { secureSettings.unregisterContentObserver(observer) }
+                        secureSettings.registerContentObserverForUserSync(SETTING, observer, userId)
+                        awaitClose { secureSettings.unregisterContentObserverSync(observer) }
                     }
                     .map { loadTilesFromSettings(userId) }
                     .flowOn(backgroundDispatcher)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableList.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableList.kt
index 08e3920..a0c9737 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableList.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/autoaddable/A11yShortcutAutoAddableList.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.qs.tiles.ColorCorrectionTile
 import com.android.systemui.qs.tiles.ColorInversionTile
+import com.android.systemui.qs.tiles.HearingDevicesTile
 import com.android.systemui.qs.tiles.OneHandedModeTile
 import com.android.systemui.qs.tiles.ReduceBrightColorsTile
 
@@ -50,6 +51,10 @@
                     TileSpec.create(ReduceBrightColorsTile.TILE_SPEC),
                     AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME
                 ),
+                factory.create(
+                    TileSpec.create(HearingDevicesTile.TILE_SPEC),
+                    AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME
+                )
             )
         } else {
             emptySet()
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 b7fcef4..97b5e87 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
@@ -43,6 +43,7 @@
 import com.android.systemui.qs.pipeline.shared.logging.QSPipelineLogger
 import com.android.systemui.qs.tiles.di.NewQSTileFactory
 import com.android.systemui.qs.toProto
+import com.android.systemui.retail.data.repository.RetailModeRepository
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.user.data.repository.UserRepository
 import com.android.systemui.util.kotlin.pairwise
@@ -137,6 +138,7 @@
     private val installedTilesComponentRepository: InstalledTilesComponentRepository,
     private val userRepository: UserRepository,
     private val minimumTilesRepository: MinimumTilesRepository,
+    private val retailModeRepository: RetailModeRepository,
     private val customTileStatePersister: CustomTileStatePersister,
     private val newQSTileFactory: Lazy<NewQSTileFactory>,
     private val tileFactory: QSFactory,
@@ -178,6 +180,14 @@
             installedTilesComponentRepository.getInstalledTilesComponents(it)
         }
 
+    private val minTiles: Int
+        get() =
+            if (retailModeRepository.inRetailMode) {
+                1
+            } else {
+                minimumTilesRepository.minNumberOfTiles
+            }
+
     init {
         if (featureFlags.pipelineEnabled) {
             startTileCollection()
@@ -273,7 +283,7 @@
                             newTileMap.filter { it.value is TileOrNotInstalled.NotInstalled }.keys,
                             newUser
                         )
-                        if (newResolvedTiles.size < minimumTilesRepository.minNumberOfTiles) {
+                        if (newResolvedTiles.size < minTiles) {
                             // We ended up with not enough tiles (some may be not installed).
                             // Prepend the default set of tiles
                             launch { tileSpecRepository.prependDefault(currentUser.value) }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index c6dfdd5..762dacd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -19,6 +19,7 @@
 import android.animation.ArgbEvaluator
 import android.animation.PropertyValuesHolder
 import android.animation.ValueAnimator
+import android.annotation.SuppressLint
 import android.content.Context
 import android.content.res.ColorStateList
 import android.content.res.Configuration
@@ -37,6 +38,7 @@
 import android.util.TypedValue
 import android.view.Gravity
 import android.view.LayoutInflater
+import android.view.MotionEvent
 import android.view.View
 import android.view.ViewGroup
 import android.view.accessibility.AccessibilityEvent
@@ -332,6 +334,21 @@
     override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
         super.onLayout(changed, l, t, r, b)
         updateHeight()
+        maybeUpdateLongPressEffectDimensions()
+    }
+
+    private fun maybeUpdateLongPressEffectDimensions() {
+        if (!isLongClickable || longPressEffect == null) return
+
+        val actualHeight = if (heightOverride != HeightOverrideable.NO_OVERRIDE) {
+            heightOverride
+        } else {
+            measuredHeight
+        }
+        initialLongPressProperties?.height = actualHeight.toFloat()
+        initialLongPressProperties?.width = measuredWidth.toFloat()
+        finalLongPressProperties?.height = LONG_PRESS_EFFECT_HEIGHT_SCALE * actualHeight
+        finalLongPressProperties?.width = LONG_PRESS_EFFECT_WIDTH_SCALE * measuredWidth
     }
 
     override fun onFocusChanged(gainFocus: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
@@ -380,15 +397,22 @@
 
     override fun init(tile: QSTile) {
         val expandable = Expandable.fromView(this)
-        init(
+        if (quickSettingsVisualHapticsLongpress()) {
+            isHapticFeedbackEnabled = false
+            longPressEffect?.qsTile = tile
+            longPressEffect?.expandable = expandable
+            init(
+                { _: View? -> longPressEffect?.onTileClick() },
+                null, // Haptics and long-clicks will be handled by the [QSLongPressEffect]
+            )
+        } else {
+            init(
                 { _: View? -> tile.click(expandable) },
                 { _: View? ->
                     tile.longClick(expandable)
                     true
-                }
-        )
-        if (quickSettingsVisualHapticsLongpress()) {
-            isHapticFeedbackEnabled = false // Haptics will be handled by the [QSLongPressEffect]
+                },
+            )
         }
     }
 
@@ -526,6 +550,20 @@
         return sb.toString()
     }
 
+    @SuppressLint("ClickableViewAccessibility")
+    override fun onTouchEvent(event: MotionEvent?): Boolean {
+        // let the View run the onTouch logic for click and long-click detection
+        val result = super.onTouchEvent(event)
+        if (longPressEffect != null) {
+            when (event?.actionMasked) {
+                MotionEvent.ACTION_DOWN -> longPressEffect.handleActionDown()
+                MotionEvent.ACTION_UP -> longPressEffect.handleActionUp()
+                MotionEvent.ACTION_CANCEL -> longPressEffect.handleActionCancel()
+            }
+        }
+        return result
+    }
+
     // HANDLE STATE CHANGES RELATED METHODS
 
     protected open fun handleStateChanged(state: QSTile.State) {
@@ -660,7 +698,6 @@
             // Long-press effects might have been enabled before but the new state does not
             // handle a long-press. In this case, we go back to the behaviour of a regular tile
             // and clean-up the resources
-            setOnTouchListener(null)
             unbindLongPressEffect()
             showRippleEffect = isClickable
             initialLongPressProperties = null
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index 76aa146..f218d86 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -16,8 +16,13 @@
 
 package com.android.systemui.qs.tiles;
 
-import static android.graphics.drawable.Icon.TYPE_URI;
 import static android.provider.Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT;
+import static android.graphics.drawable.Icon.TYPE_URI;
+import static android.graphics.drawable.Icon.TYPE_URI_ADAPTIVE_BITMAP;
+import static android.graphics.drawable.Icon.TYPE_RESOURCE;
+import static android.graphics.drawable.Icon.TYPE_BITMAP;
+import static android.graphics.drawable.Icon.TYPE_ADAPTIVE_BITMAP;
+import static android.graphics.drawable.Icon.TYPE_DATA;
 
 import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE;
 import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_WALLET_APP_CHANGE;
@@ -237,11 +242,21 @@
                 return;
             }
             mSelectedCard = cards.get(selectedIndex);
-            android.graphics.drawable.Icon cardImageIcon = mSelectedCard.getCardImage();
-            if (cardImageIcon.getType() == TYPE_URI) {
-                mCardViewDrawable = null;
-            } else {
-                mCardViewDrawable = mSelectedCard.getCardImage().loadDrawable(mContext);
+            switch (mSelectedCard.getCardImage().getType()) {
+                case TYPE_URI:
+                case TYPE_URI_ADAPTIVE_BITMAP:
+                    mCardViewDrawable = null;
+                    break;
+                case TYPE_RESOURCE:
+                case TYPE_BITMAP:
+                case TYPE_ADAPTIVE_BITMAP:
+                case TYPE_DATA:
+                    mCardViewDrawable = mSelectedCard.getCardImage().loadDrawable(mContext);
+                    break;
+                default:
+                    Log.e(TAG, "Unknown icon type: " + mSelectedCard.getCardImage().getType());
+                    mCardViewDrawable = null;
+                    break;
             }
             refreshState();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
index 9937ea4..e680102 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
@@ -46,11 +46,14 @@
 import com.android.systemui.recordissue.IssueRecordingService
 import com.android.systemui.recordissue.IssueRecordingState
 import com.android.systemui.recordissue.RecordIssueDialogDelegate
+import com.android.systemui.recordissue.TraceurMessageSender
 import com.android.systemui.res.R
 import com.android.systemui.screenrecord.RecordingService
 import com.android.systemui.settings.UserContextProvider
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil
 import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.traceur.TraceUtils.PresetTraceType
+import java.util.concurrent.Executor
 import javax.inject.Inject
 
 class RecordIssueTile
@@ -70,6 +73,8 @@
     private val dialogTransitionAnimator: DialogTransitionAnimator,
     private val panelInteractor: PanelInteractor,
     private val userContextProvider: UserContextProvider,
+    private val traceurMessageSender: TraceurMessageSender,
+    @Background private val bgExecutor: Executor,
     private val issueRecordingState: IssueRecordingState,
     private val delegateFactory: RecordIssueDialogDelegate.Factory,
 ) :
@@ -96,6 +101,11 @@
         }
     }
 
+    override fun handleDestroy() {
+        super.handleDestroy()
+        bgExecutor.execute { traceurMessageSender.unbindFromTraceur(mContext) }
+    }
+
     override fun getTileLabel(): CharSequence = mContext.getString(R.string.qs_record_issue_label)
 
     /**
@@ -121,14 +131,14 @@
         }
     }
 
-    private fun startIssueRecordingService(screenRecord: Boolean, winscopeTracing: Boolean) =
+    private fun startIssueRecordingService(screenRecord: Boolean, traceType: PresetTraceType) =
         PendingIntent.getForegroundService(
                 userContextProvider.userContext,
                 RecordingService.REQUEST_CODE,
                 IssueRecordingService.getStartIntent(
                     userContextProvider.userContext,
                     screenRecord,
-                    winscopeTracing
+                    traceType
                 ),
                 PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
             )
@@ -147,7 +157,7 @@
         val dialog: AlertDialog =
             delegateFactory
                 .create {
-                    startIssueRecordingService(it.screenRecord, it.winscopeTracing)
+                    startIssueRecordingService(it.screenRecord, it.traceType)
                     dialogTransitionAnimator.disableAllCurrentDialogsExitAnimations()
                     panelInteractor.collapsePanels()
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index b057476..c091ac3de 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -118,6 +118,8 @@
 public class InternetDialogController implements AccessPointController.AccessPointCallback {
 
     private static final String TAG = "InternetDialogController";
+    private static final String ACTION_NETWORK_PROVIDER_SETTINGS =
+            "android.settings.NETWORK_PROVIDER_SETTINGS";
     private static final String ACTION_WIFI_SCANNING_SETTINGS =
             "android.settings.WIFI_SCANNING_SETTINGS";
     /**
@@ -361,8 +363,7 @@
 
     @VisibleForTesting
     protected Intent getSettingsIntent() {
-        return new Intent(Settings.ACTION_NETWORK_PROVIDER_SETTINGS).addFlags(
-                Intent.FLAG_ACTIVITY_NEW_TASK);
+        return new Intent(ACTION_NETWORK_PROVIDER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
     }
 
     @Nullable
@@ -935,6 +936,10 @@
             mHasActiveSubIdOnDds = false;
             Log.e(TAG, "Can't get DDS subscriptionInfo");
             return;
+        } else if (ddsSubInfo.isOnlyNonTerrestrialNetwork()) {
+            mHasActiveSubIdOnDds = false;
+            Log.d(TAG, "This is NTN, so do not show mobile data");
+            return;
         }
 
         mHasActiveSubIdOnDds = isEmbeddedSubscriptionVisible(ddsSubInfo);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
index fb872d5..c7326b08 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/adapter/QSSceneAdapter.kt
@@ -46,7 +46,6 @@
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.BufferOverflow
-import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.SharingStarted
@@ -89,8 +88,10 @@
     /**
      * A view with the QS content ([QSContainerImpl]), managed by an instance of [QSImpl] tracked by
      * the interactor.
+     *
+     * A null value means that there is no inflated view yet. See [inflate].
      */
-    val qsView: Flow<View>
+    val qsView: StateFlow<View?>
 
     /** Sets the [MirrorController] in [QSImpl]. Set to `null` to remove. */
     fun setBrightnessMirrorController(mirrorController: MirrorController?)
@@ -101,9 +102,22 @@
      */
     suspend fun inflate(context: Context)
 
-    /** Set the current state for QS. [state]. */
+    /**
+     * Set the current state for QS. [state].
+     *
+     * This will not trigger expansion (animation between QQS or QS) or squishiness to be applied.
+     * For that, use [applyLatestExpansionAndSquishiness] outside of the composition phase.
+     */
     fun setState(state: State)
 
+    /**
+     * Explicitly applies the expansion and squishiness value from the latest state set. Call this
+     * only outside of the composition phase as this will call [QSImpl.setQsExpansion] that is
+     * normally called during animations. In particular, this will read the value of
+     * [State.squishiness], that is not safe to read in the composition phase.
+     */
+    fun applyLatestExpansionAndSquishiness()
+
     /** Propagates the bottom nav bar size to [QSImpl] to be used as necessary. */
     suspend fun applyBottomNavBarPadding(padding: Int)
 
@@ -141,14 +155,24 @@
             override val squishiness = { 1f }
         }
 
-        /** State for appearing QQS from Lockscreen or Gone */
-        data class UnsquishingQQS(override val squishiness: () -> Float) : State {
+        /**
+         * State for appearing QQS from Lockscreen or Gone.
+         *
+         * This should not be a data class, as it has a method parameter and even if it's the same
+         * lambda the output value may have changed.
+         */
+        class UnsquishingQQS(override val squishiness: () -> Float) : State {
             override val isVisible = true
             override val expansion = 0f
         }
 
-        /** State for appearing QS from Lockscreen or Gone, used in Split shade */
-        data class UnsquishingQS(override val squishiness: () -> Float) : State {
+        /**
+         * State for appearing QS from Lockscreen or Gone, used in Split shade.
+         *
+         * This should not be a data class, as it has a method parameter and even if it's the same
+         * lambda the output value may have changed.
+         */
+        class UnsquishingQS(override val squishiness: () -> Float) : State {
             override val isVisible = true
             override val expansion = 1f
         }
@@ -236,7 +260,10 @@
 
     private val _qsImpl: MutableStateFlow<QSImpl?> = MutableStateFlow(null)
     val qsImpl = _qsImpl.asStateFlow()
-    override val qsView: Flow<View> = _qsImpl.map { it?.view }.filterNotNull()
+    override val qsView: StateFlow<View?> =
+        _qsImpl
+            .map { it?.view }
+            .stateIn(applicationScope, SharingStarted.WhileSubscribed(), _qsImpl.value?.view)
 
     override val qqsHeight: Int
         get() = qsImpl.value?.qqsHeight ?: 0
@@ -370,7 +397,12 @@
         setQsVisible(state.isVisible)
         setExpanded(state.isVisible && state.expansion > 0f)
         setListening(state.isVisible)
-        setQsExpansion(state.expansion, 1f, 0f, state.squishiness())
+    }
+
+    override fun applyLatestExpansionAndSquishiness() {
+        val qsImpl = _qsImpl.value
+        val state = state.value
+        qsImpl?.setQsExpansion(state.expansion, 1f, 0f, state.squishiness())
     }
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt
index d48d55d..c1a5646 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsShadeSceneViewModel.kt
@@ -21,8 +21,12 @@
 import com.android.compose.animation.scene.Swipe
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
+import com.android.systemui.brightness.ui.viewmodel.BrightnessSliderViewModel
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.qs.panels.ui.viewmodel.EditModeViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.TileGridViewModel
+import com.android.systemui.qs.ui.adapter.QSSceneAdapter
 import com.android.systemui.shade.ui.viewmodel.OverlayShadeViewModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -37,7 +41,11 @@
 @Inject
 constructor(
     @Application private val applicationScope: CoroutineScope,
-    overlayShadeViewModel: OverlayShadeViewModel,
+    val overlayShadeViewModel: OverlayShadeViewModel,
+    val brightnessSliderViewModel: BrightnessSliderViewModel,
+    val tileGridViewModel: TileGridViewModel,
+    val editModeViewModel: EditModeViewModel,
+    val qsSceneAdapter: QSSceneAdapter,
 ) {
     val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> =
         overlayShadeViewModel.backgroundScene
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt
index bb3b654..23dbc26 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingConfig.kt
@@ -16,4 +16,6 @@
 
 package com.android.systemui.recordissue
 
-data class IssueRecordingConfig(val screenRecord: Boolean, val winscopeTracing: Boolean)
+import com.android.traceur.TraceUtils.PresetTraceType
+
+data class IssueRecordingConfig(val screenRecord: Boolean, val traceType: PresetTraceType)
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
index 6694878..5ede64a 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/IssueRecordingService.kt
@@ -24,8 +24,6 @@
 import android.net.Uri
 import android.os.Handler
 import android.os.UserHandle
-import android.util.Log
-import androidx.core.content.FileProvider
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.dagger.qualifiers.LongRunning
@@ -37,16 +35,10 @@
 import com.android.systemui.screenrecord.RecordingServiceStrings
 import com.android.systemui.settings.UserContextProvider
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil
-import com.android.traceur.FileSender
-import com.android.traceur.TraceUtils
-import java.io.File
-import java.io.FileOutputStream
-import java.nio.file.Files
+import com.android.traceur.MessageConstants.INTENT_EXTRA_TRACE_TYPE
+import com.android.traceur.TraceUtils.PresetTraceType
 import java.util.concurrent.Executor
-import java.util.zip.ZipEntry
-import java.util.zip.ZipOutputStream
 import javax.inject.Inject
-import kotlin.jvm.optionals.getOrElse
 
 class IssueRecordingService
 @Inject
@@ -60,6 +52,7 @@
     keyguardDismissUtil: KeyguardDismissUtil,
     private val dialogTransitionAnimator: DialogTransitionAnimator,
     private val panelInteractor: PanelInteractor,
+    private val traceurMessageSender: TraceurMessageSender,
     private val issueRecordingState: IssueRecordingState,
     private val iActivityManager: IActivityManager,
 ) :
@@ -82,17 +75,14 @@
     override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
         when (intent?.action) {
             ACTION_START -> {
-                TraceUtils.traceStart(
-                    this,
-                    DEFAULT_TRACE_TAGS,
-                    DEFAULT_BUFFER_SIZE,
-                    DEFAULT_IS_INCLUDING_WINSCOPE,
-                    DEFAULT_IS_INCLUDING_APP_TRACE,
-                    DEFAULT_IS_LONG_TRACE,
-                    DEFAULT_ATTACH_TO_BUGREPORT,
-                    DEFAULT_MAX_TRACE_SIZE,
-                    DEFAULT_MAX_TRACE_DURATION_IN_MINUTES
-                )
+                bgExecutor.execute {
+                    traceurMessageSender.startTracing(
+                        intent.getSerializableExtra(
+                            INTENT_EXTRA_TRACE_TYPE,
+                            PresetTraceType::class.java
+                        )
+                    )
+                }
                 issueRecordingState.isRecording = true
                 if (!intent.getBooleanExtra(EXTRA_SCREEN_RECORD, false)) {
                     // If we don't want to record the screen, the ACTION_SHOW_START_NOTIF action
@@ -102,7 +92,10 @@
             }
             ACTION_STOP,
             ACTION_STOP_NOTIF -> {
-                TraceUtils.traceStop(this)
+                // ViewCapture needs to save it's data before it is disabled, or else the data will
+                // be lost. This is expected to change in the near future, and when that happens
+                // this line should be removed.
+                bgExecutor.execute { traceurMessageSender.stopTracing() }
                 issueRecordingState.isRecording = false
             }
             ACTION_SHARE -> {
@@ -117,7 +110,7 @@
                     if (issueRecordingState.takeBugReport) {
                         iActivityManager.requestBugReportWithExtraAttachment(screenRecording)
                     } else {
-                        shareRecording(screenRecording)
+                        traceurMessageSender.shareTraces(applicationContext, screenRecording)
                     }
                 }
 
@@ -134,75 +127,10 @@
         return super.onStartCommand(intent, flags, startId)
     }
 
-    private fun shareRecording(screenRecording: Uri?) {
-        val traces =
-            TraceUtils.traceDump(this, TRACE_FILE_NAME).getOrElse {
-                Log.v(
-                    TAG,
-                    "Traces were not present. This can happen if users double" +
-                        "click on share notification. Traces are cleaned up after sharing" +
-                        "so they won't be present for the 2nd share attempt."
-                )
-                return
-            }
-        val perfetto = FileProvider.getUriForFile(this, AUTHORITY, traces.first())
-        val urisToShare = mutableListOf(perfetto)
-        traces.removeFirst()
-
-        getZipWinscopeFileUri(traces)?.let { urisToShare.add(it) }
-        screenRecording?.let { urisToShare.add(it) }
-
-        val sendIntent =
-            FileSender.buildSendIntent(this, urisToShare).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-
-        // TODO: Debug why the notification shade isn't closing upon starting the BetterBug activity
-        mKeyguardDismissUtil.executeWhenUnlocked(
-            {
-                startActivity(sendIntent)
-                false
-            },
-            false,
-            false
-        )
-    }
-
-    private fun getZipWinscopeFileUri(traceFiles: List<File>): Uri? {
-        try {
-            externalCacheDir?.mkdirs()
-            val outZip: File = File.createTempFile(TEMP_FILE_PREFIX, ZIP_SUFFIX, externalCacheDir)
-            ZipOutputStream(FileOutputStream(outZip)).use { os ->
-                traceFiles.forEach { file ->
-                    os.putNextEntry(ZipEntry(file.name))
-                    Files.copy(file.toPath(), os)
-                    os.closeEntry()
-                }
-            }
-            return FileProvider.getUriForFile(this, AUTHORITY, outZip)
-        } catch (e: Exception) {
-            Log.e(TAG, "Failed to zip and package Recordings. Cannot share with BetterBug.", e)
-            return null
-        }
-    }
-
     companion object {
         private const val TAG = "IssueRecordingService"
         private const val CHANNEL_ID = "issue_record"
         private const val EXTRA_SCREEN_RECORD = "extra_screenRecord"
-        private const val EXTRA_WINSCOPE_TRACING = "extra_winscopeTracing"
-        private const val ZIP_SUFFIX = ".zip"
-        private const val TEMP_FILE_PREFIX = "winscope_recordings"
-
-        private val DEFAULT_TRACE_TAGS = listOf<String>()
-        private const val DEFAULT_BUFFER_SIZE = 16384
-        private const val DEFAULT_IS_INCLUDING_WINSCOPE = true
-        private const val DEFAULT_IS_LONG_TRACE = false
-        private const val DEFAULT_IS_INCLUDING_APP_TRACE = true
-        private const val DEFAULT_ATTACH_TO_BUGREPORT = true
-        private const val DEFAULT_MAX_TRACE_SIZE = 10240
-        private const val DEFAULT_MAX_TRACE_DURATION_IN_MINUTES = 30
-
-        private val TRACE_FILE_NAME = TraceUtils.getOutputFilename(TraceUtils.RecordingType.TRACE)
-        private const val AUTHORITY = "com.android.systemui.fileprovider"
 
         /**
          * Get an intent to stop the issue recording service.
@@ -223,12 +151,12 @@
         fun getStartIntent(
             context: Context,
             screenRecord: Boolean,
-            winscopeTracing: Boolean,
+            traceType: PresetTraceType,
         ): Intent =
             Intent(context, IssueRecordingService::class.java)
                 .setAction(ACTION_START)
                 .putExtra(EXTRA_SCREEN_RECORD, screenRecord)
-                .putExtra(EXTRA_WINSCOPE_TRACING, winscopeTracing)
+                .putExtra(INTENT_EXTRA_TRACE_TYPE, traceType)
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
index 68b8836..84a063a 100644
--- a/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/RecordIssueDialogDelegate.kt
@@ -19,6 +19,7 @@
 import android.annotation.SuppressLint
 import android.app.AlertDialog
 import android.content.Context
+import android.content.Intent
 import android.content.res.ColorStateList
 import android.graphics.Color
 import android.os.Bundle
@@ -45,6 +46,8 @@
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.traceur.MessageConstants.INTENT_EXTRA_TRACE_TYPE
+import com.android.traceur.TraceUtils.PresetTraceType
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
@@ -64,9 +67,19 @@
     private val userFileManager: UserFileManager,
     private val screenCaptureDisabledDialogDelegate: ScreenCaptureDisabledDialogDelegate,
     private val issueRecordingState: IssueRecordingState,
+    private val traceurMessageSender: TraceurMessageSender,
     @Assisted private val onStarted: Consumer<IssueRecordingConfig>,
 ) : SystemUIDialog.Delegate {
 
+    private val issueTypeOptions: Map<Int, PresetTraceType> =
+        hashMapOf(
+            Pair(R.string.performance, PresetTraceType.PERFORMANCE),
+            Pair(R.string.user_interface, PresetTraceType.UI),
+            Pair(R.string.battery, PresetTraceType.BATTERY),
+            Pair(R.string.thermal, PresetTraceType.THERMAL)
+        )
+    private var selectedIssueType: PresetTraceType? = null
+
     /** To inject dependencies and allow for easier testing */
     @AssistedFactory
     interface Factory {
@@ -92,7 +105,7 @@
                     onStarted.accept(
                         IssueRecordingConfig(
                             screenRecordSwitch.isChecked,
-                            true /* TODO: Base this on issueType selected */
+                            selectedIssueType ?: PresetTraceType.UNSET
                         )
                     )
                     dismiss()
@@ -100,6 +113,7 @@
                 false
             )
         }
+        bgExecutor.execute { traceurMessageSender.bindToTraceur(dialog.context) }
     }
 
     override fun createDialog(): SystemUIDialog = factory.create(this)
@@ -166,20 +180,25 @@
 
     @MainThread
     private fun onIssueTypeClicked(context: Context, onIssueTypeSelected: Runnable) {
-        val selectedCategory = issueTypeButton.text.toString()
         val popupMenu = PopupMenu(context, issueTypeButton)
 
-        context.resources.getStringArray(R.array.qs_record_issue_types).forEachIndexed { i, cat ->
-            popupMenu.menu.add(0, 0, i, cat).apply {
+        issueTypeOptions.keys.forEach {
+            popupMenu.menu.add(it).apply {
                 setIcon(R.drawable.arrow_pointing_down)
-                if (selectedCategory != cat) {
+                if (issueTypeOptions[it] != selectedIssueType) {
                     iconTintList = ColorStateList.valueOf(Color.TRANSPARENT)
                 }
+                intent = Intent().putExtra(INTENT_EXTRA_TRACE_TYPE, issueTypeOptions[it])
             }
         }
         popupMenu.apply {
             setOnMenuItemClickListener {
                 issueTypeButton.text = it.title
+                selectedIssueType =
+                    it.intent?.getSerializableExtra(
+                        INTENT_EXTRA_TRACE_TYPE,
+                        PresetTraceType::class.java
+                    )
                 onIssueTypeSelected.run()
                 true
             }
diff --git a/packages/SystemUI/src/com/android/systemui/recordissue/TraceurMessageSender.kt b/packages/SystemUI/src/com/android/systemui/recordissue/TraceurMessageSender.kt
new file mode 100644
index 0000000..189336c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recordissue/TraceurMessageSender.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recordissue
+
+import android.annotation.SuppressLint
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.content.ServiceConnection
+import android.content.pm.PackageManager
+import android.net.Uri
+import android.os.Bundle
+import android.os.Handler
+import android.os.IBinder
+import android.os.Looper
+import android.os.Message
+import android.os.Messenger
+import android.util.Log
+import androidx.annotation.WorkerThread
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.traceur.FileSender
+import com.android.traceur.MessageConstants
+import com.android.traceur.TraceUtils.PresetTraceType
+import javax.inject.Inject
+
+private const val TAG = "TraceurMessageSender"
+
+@SysUISingleton
+class TraceurMessageSender @Inject constructor(@Background private val backgroundLooper: Looper) {
+    private var binder: Messenger? = null
+    private var isBound: Boolean = false
+
+    private val traceurConnection =
+        object : ServiceConnection {
+            override fun onServiceConnected(className: ComponentName, service: IBinder) {
+                binder = Messenger(service)
+                isBound = true
+            }
+
+            override fun onServiceDisconnected(className: ComponentName) {
+                binder = null
+                isBound = false
+            }
+        }
+
+    @SuppressLint("WrongConstant")
+    @WorkerThread
+    fun bindToTraceur(context: Context) {
+        if (isBound) {
+            // Binding needs to happen after the phone has been unlocked. The RecordIssueTile is
+            // initialized before this happens though, so binding is placed at a later time, during
+            // normal operations that can be repeated. This check avoids calling "bindService" 2x+
+            return
+        }
+        try {
+            val info =
+                context.packageManager.getPackageInfo(
+                    MessageConstants.TRACING_APP_PACKAGE_NAME,
+                    PackageManager.MATCH_SYSTEM_ONLY
+                )
+            val intent =
+                Intent().setClassName(info.packageName, MessageConstants.TRACING_APP_ACTIVITY)
+            val flags =
+                Context.BIND_AUTO_CREATE or
+                    Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE or
+                    Context.BIND_WAIVE_PRIORITY
+            context.bindService(intent, traceurConnection, flags)
+        } catch (e: Exception) {
+            Log.e(TAG, "failed to bind to Traceur's service", e)
+        }
+    }
+
+    @WorkerThread
+    fun unbindFromTraceur(context: Context) {
+        if (isBound) {
+            context.unbindService(traceurConnection)
+        }
+    }
+
+    @WorkerThread
+    fun startTracing(traceType: PresetTraceType?) {
+        val data =
+            Bundle().apply { putSerializable(MessageConstants.INTENT_EXTRA_TRACE_TYPE, traceType) }
+        notifyTraceur(MessageConstants.START_WHAT, data)
+    }
+
+    @WorkerThread fun stopTracing() = notifyTraceur(MessageConstants.STOP_WHAT)
+
+    @WorkerThread
+    fun shareTraces(context: Context, screenRecord: Uri?) {
+        val replyHandler = Messenger(TraceurMessageHandler(context, screenRecord, backgroundLooper))
+        notifyTraceur(MessageConstants.SHARE_WHAT, replyTo = replyHandler)
+    }
+
+    @WorkerThread
+    private fun notifyTraceur(what: Int, data: Bundle = Bundle(), replyTo: Messenger? = null) {
+        try {
+            binder!!.send(
+                Message.obtain().apply {
+                    this.what = what
+                    this.data = data
+                    this.replyTo = replyTo
+                }
+            )
+        } catch (e: Exception) {
+            Log.e(TAG, "failed to notify Traceur", e)
+        }
+    }
+
+    private class TraceurMessageHandler(
+        private val context: Context,
+        private val screenRecord: Uri?,
+        looper: Looper,
+    ) : Handler(looper) {
+
+        override fun handleMessage(msg: Message) {
+            if (MessageConstants.SHARE_WHAT == msg.what) {
+                shareTraces(
+                    msg.data.getParcelable(MessageConstants.EXTRA_PERFETTO, Uri::class.java),
+                    msg.data.getParcelable(MessageConstants.EXTRA_WINSCOPE, Uri::class.java)
+                )
+            } else {
+                throw IllegalArgumentException("received unknown msg.what: " + msg.what)
+            }
+        }
+
+        private fun shareTraces(perfetto: Uri?, winscope: Uri?) {
+            val uris: List<Uri> =
+                mutableListOf<Uri>().apply {
+                    perfetto?.let { add(it) }
+                    winscope?.let { add(it) }
+                    screenRecord?.let { add(it) }
+                }
+            val fileSharingIntent =
+                FileSender.buildSendIntent(context, uris)
+                    .addFlags(
+                        Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT
+                    )
+            context.startActivity(fileSharingIntent)
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/retail/data/repository/RetailModeRepository.kt b/packages/SystemUI/src/com/android/systemui/retail/data/repository/RetailModeRepository.kt
index 3c0aa38..09fd7df 100644
--- a/packages/SystemUI/src/com/android/systemui/retail/data/repository/RetailModeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/retail/data/repository/RetailModeRepository.kt
@@ -66,9 +66,9 @@
                         }
                     }
 
-                globalSettings.registerContentObserver(RETAIL_MODE_SETTING, observer)
+                globalSettings.registerContentObserverSync(RETAIL_MODE_SETTING, observer)
 
-                awaitClose { globalSettings.unregisterContentObserver(observer) }
+                awaitClose { globalSettings.unregisterContentObserverSync(observer) }
             }
             .onStart { emit(Unit) }
             .map { globalSettings.getInt(RETAIL_MODE_SETTING, 0) != 0 }
diff --git a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
index 8169dec..7a9d09a 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/KeyguardlessSceneContainerFrameworkModule.kt
@@ -20,6 +20,7 @@
 import com.android.systemui.notifications.ui.composable.NotificationsShadeSessionModule
 import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
 import com.android.systemui.scene.domain.startable.SceneContainerStartable
+import com.android.systemui.scene.domain.startable.ScrimStartable
 import com.android.systemui.scene.shared.model.SceneContainerConfig
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.shared.flag.DualShade
@@ -50,6 +51,11 @@
 
     @Binds
     @IntoMap
+    @ClassKey(ScrimStartable::class)
+    fun scrimStartable(impl: ScrimStartable): CoreStartable
+
+    @Binds
+    @IntoMap
     @ClassKey(WindowRootViewVisibilityInteractor::class)
     fun bindWindowRootViewVisibilityInteractor(
         impl: WindowRootViewVisibilityInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
index 9bd2694..7e6dfb8 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.notifications.ui.composable.NotificationsShadeSessionModule
 import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
 import com.android.systemui.scene.domain.startable.SceneContainerStartable
+import com.android.systemui.scene.domain.startable.ScrimStartable
 import com.android.systemui.scene.shared.model.SceneContainerConfig
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.shade.shared.flag.DualShade
@@ -56,6 +57,11 @@
 
     @Binds
     @IntoMap
+    @ClassKey(ScrimStartable::class)
+    fun scrimStartable(impl: ScrimStartable): CoreStartable
+
+    @Binds
+    @IntoMap
     @ClassKey(WindowRootViewVisibilityInteractor::class)
     fun bindWindowRootViewVisibilityInteractor(
         impl: WindowRootViewVisibilityInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
new file mode 100644
index 0000000..c6f51b3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/ScrimStartable.kt
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.scene.domain.startable
+
+import androidx.annotation.VisibleForTesting
+import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.compose.animation.scene.SceneKey
+import com.android.systemui.CoreStartable
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
+import com.android.systemui.keyguard.domain.interactor.BiometricUnlockInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor
+import com.android.systemui.statusbar.phone.DozeServiceHost
+import com.android.systemui.statusbar.phone.ScrimController
+import com.android.systemui.statusbar.phone.ScrimState
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class ScrimStartable
+@Inject
+constructor(
+    @Application private val applicationScope: CoroutineScope,
+    private val scrimController: ScrimController,
+    sceneInteractor: SceneInteractor,
+    deviceEntryInteractor: DeviceEntryInteractor,
+    keyguardInteractor: KeyguardInteractor,
+    occlusionInteractor: SceneContainerOcclusionInteractor,
+    biometricUnlockInteractor: BiometricUnlockInteractor,
+    private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
+    private val alternateBouncerInteractor: AlternateBouncerInteractor,
+    brightnessMirrorShowingInteractor: BrightnessMirrorShowingInteractor,
+    private val dozeServiceHost: DozeServiceHost,
+) : CoreStartable {
+
+    @VisibleForTesting
+    val scrimState: Flow<ScrimState?> =
+        combine(
+                deviceEntryInteractor.isDeviceEntered,
+                occlusionInteractor.invisibleDueToOcclusion,
+                sceneInteractor.currentScene,
+                sceneInteractor.transitionState,
+                keyguardInteractor.isDozing,
+                keyguardInteractor.isDreaming,
+                biometricUnlockInteractor.unlockState,
+                brightnessMirrorShowingInteractor.isShowing,
+                keyguardInteractor.isPulsing,
+                conflatedCallbackFlow {
+                    val listener =
+                        DozeServiceHost.HasPendingScreenOffCallbackChangeListener {
+                            hasPendingScreenOffCallback ->
+                            trySend(hasPendingScreenOffCallback)
+                        }
+                    dozeServiceHost.setHasPendingScreenOffCallbackChangeListener(listener)
+                    awaitClose {
+                        dozeServiceHost.setHasPendingScreenOffCallbackChangeListener(null)
+                    }
+                },
+            ) { flowValues ->
+                val isDeviceEntered = flowValues[0] as Boolean
+                val isOccluded = flowValues[1] as Boolean
+                val currentScene = flowValues[2] as SceneKey
+                val transitionState = flowValues[3] as ObservableTransitionState
+                val isDozing = flowValues[4] as Boolean
+                val isDreaming = flowValues[5] as Boolean
+                val biometricUnlockState = flowValues[6] as BiometricUnlockModel
+                val isBrightnessMirrorVisible = flowValues[7] as Boolean
+                val isPulsing = flowValues[8] as Boolean
+                val hasPendingScreenOffCallback = flowValues[9] as Boolean
+
+                // This is true when the lockscreen scene is either the current scene or somewhere
+                // in the
+                // navigation back stack of scenes.
+                val isOnKeyguard = !isDeviceEntered
+                val isCurrentSceneBouncer = currentScene == Scenes.Bouncer
+                // This is true when moving away from one of the keyguard scenes to the gone scene.
+                // It
+                // happens only when unlocking or when dismissing a dismissible lockscreen.
+                val isTransitioningAwayFromKeyguard =
+                    transitionState is ObservableTransitionState.Transition &&
+                        transitionState.fromScene.isKeyguard() &&
+                        transitionState.toScene == Scenes.Gone
+
+                // This is true when any of the shade scenes is the current scene.
+                val isCurrentSceneShade = currentScene.isShade()
+                // This is true when moving into one of the shade scenes when a non-shade scene.
+                val isTransitioningToShade =
+                    transitionState is ObservableTransitionState.Transition &&
+                        !transitionState.fromScene.isShade() &&
+                        transitionState.toScene.isShade()
+
+                // This is true after completing a transition to communal.
+                val isIdleOnCommunal = transitionState.isIdle(Scenes.Communal)
+
+                // This is true during the process of an unlock of the device.
+                // TODO(b/330587738): add support for remote unlock animations. If such an
+                //   animation is underway, unlocking should be true.
+                val unlocking =
+                    isOnKeyguard &&
+                        (biometricUnlockState.mode == BiometricUnlockMode.WAKE_AND_UNLOCK ||
+                            isTransitioningAwayFromKeyguard)
+
+                if (alternateBouncerInteractor.isVisibleState()) {
+                    // This will cancel the keyguardFadingAway animation if it is running. We need
+                    // to do
+                    // this as otherwise it can remain pending and leave keyguard in a weird state.
+                    onKeyguardFadedAway(isTransitioningAwayFromKeyguard)
+                    if (!isTransitioningToShade) {
+                        // Safeguard which prevents the scrim from being stuck in the wrong
+                        // state
+                        Model(scrimState = ScrimState.KEYGUARD, unlocking = unlocking)
+                    } else {
+                        // Assume scrim state for shade is already correct and do nothing
+                        null
+                    }
+                } else if (isCurrentSceneBouncer && !unlocking) {
+                    // Bouncer needs the front scrim when it's on top of an activity, tapping on a
+                    // notification, editing QS or being dismissed by
+                    // FLAG_DISMISS_KEYGUARD_ACTIVITY.
+                    Model(
+                        scrimState =
+                            if (statusBarKeyguardViewManager.primaryBouncerNeedsScrimming()) {
+                                ScrimState.BOUNCER_SCRIMMED
+                            } else {
+                                ScrimState.BOUNCER
+                            },
+                        unlocking = false,
+                    )
+                } else if (isBrightnessMirrorVisible) {
+                    Model(scrimState = ScrimState.BRIGHTNESS_MIRROR, unlocking = unlocking)
+                } else if (isCurrentSceneShade && !isDeviceEntered) {
+                    Model(scrimState = ScrimState.SHADE_LOCKED, unlocking = unlocking)
+                } else if (isPulsing) {
+                    Model(scrimState = ScrimState.PULSING, unlocking = unlocking)
+                } else if (hasPendingScreenOffCallback) {
+                    Model(scrimState = ScrimState.OFF, unlocking = unlocking)
+                } else if (isDozing && !unlocking) {
+                    // This will cancel the keyguardFadingAway animation if it is running. We need
+                    // to do
+                    // this as otherwise it can remain pending and leave keyguard in a weird state.
+                    onKeyguardFadedAway(isTransitioningAwayFromKeyguard)
+                    Model(scrimState = ScrimState.AOD, unlocking = false)
+                } else if (isIdleOnCommunal) {
+                    if (isOnKeyguard && isDreaming && !unlocking) {
+                        Model(scrimState = ScrimState.GLANCEABLE_HUB_OVER_DREAM, unlocking = false)
+                    } else {
+                        Model(scrimState = ScrimState.GLANCEABLE_HUB, unlocking = unlocking)
+                    }
+                } else if (isOnKeyguard && !unlocking && !isOccluded) {
+                    Model(scrimState = ScrimState.KEYGUARD, unlocking = false)
+                } else if (isOnKeyguard && !unlocking && isDreaming) {
+                    Model(scrimState = ScrimState.DREAMING, unlocking = false)
+                } else {
+                    Model(scrimState = ScrimState.UNLOCKED, unlocking = unlocking)
+                }
+            }
+            .onEach { model ->
+                if (model != null) {
+                    scrimController.setExpansionAffectsAlpha(!model.unlocking)
+                }
+            }
+            .map { model -> model?.scrimState }
+
+    override fun start() {
+        if (!SceneContainerFlag.isEnabled) {
+            return
+        }
+
+        hydrateScrimState()
+    }
+
+    private fun hydrateScrimState() {
+        applicationScope.launch {
+            scrimState.filterNotNull().collect { scrimState ->
+                scrimController.transitionTo(scrimState)
+            }
+        }
+    }
+
+    private fun onKeyguardFadedAway(isKeyguardGoingAway: Boolean) {
+        if (isKeyguardGoingAway) {
+            statusBarKeyguardViewManager.onKeyguardFadedAway()
+        }
+    }
+
+    private fun SceneKey.isKeyguard(): Boolean {
+        return this == Scenes.Lockscreen || this == Scenes.Bouncer
+    }
+
+    private fun SceneKey.isShade(): Boolean {
+        return this == Scenes.Shade ||
+            this == Scenes.QuickSettings ||
+            this == Scenes.NotificationsShade ||
+            this == Scenes.QuickSettingsShade
+    }
+
+    private data class Model(
+        val scrimState: ScrimState,
+        val unlocking: Boolean,
+    )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index 5469a4e..e024710 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -119,6 +119,7 @@
         IMediaProjection projection = IMediaProjection.Stub.asInterface(proj.asBinder());
         if (mCaptureRegion != null) {
             projection.setLaunchCookie(mCaptureRegion.getLaunchCookie());
+            projection.setTaskId(mCaptureRegion.getTaskId());
         }
         mMediaProjection = new MediaProjection(mContext, projection);
         mMediaProjection.registerCallback(this, mHandler);
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index 37f2a21..49810762 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -137,14 +137,14 @@
         public void startObserving() {
             if (!mObserving) {
                 mObserving = true;
-                mSecureSettings.registerContentObserverForUser(
+                mSecureSettings.registerContentObserverForUserSync(
                         BRIGHTNESS_MODE_URI,
                         false, this, UserHandle.USER_ALL);
             }
         }
 
         public void stopObserving() {
-            mSecureSettings.unregisterContentObserver(this);
+            mSecureSettings.unregisterContentObserverSync(this);
             mObserving = false;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
index 22aa492..bf0843b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/GlanceableHubContainerController.kt
@@ -20,10 +20,12 @@
 import android.graphics.Rect
 import android.os.PowerManager
 import android.os.SystemClock
+import android.util.ArraySet
 import android.view.GestureDetector
 import android.view.MotionEvent
 import android.view.View
 import android.view.ViewGroup
+import android.widget.FrameLayout
 import androidx.activity.OnBackPressedDispatcher
 import androidx.activity.OnBackPressedDispatcherOwner
 import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
@@ -35,6 +37,7 @@
 import androidx.lifecycle.repeatOnLifecycle
 import com.android.compose.theme.PlatformTheme
 import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.Flags.glanceableHubFullscreenSwipe
 import com.android.systemui.ambient.touch.TouchMonitor
 import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent
 import com.android.systemui.communal.dagger.Communal
@@ -43,6 +46,7 @@
 import com.android.systemui.communal.ui.compose.CommunalContent
 import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
 import com.android.systemui.communal.util.CommunalColors
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -51,10 +55,12 @@
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
 import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
 import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
 import com.android.systemui.util.kotlin.BooleanFlowOperators.not
 import com.android.systemui.util.kotlin.collectFlow
+import java.util.function.Consumer
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.launch
@@ -64,6 +70,7 @@
  *
  * This will be used until the glanceable hub is integrated into Flexiglass.
  */
+@SysUISingleton
 class GlanceableHubContainerController
 @Inject
 constructor(
@@ -75,11 +82,38 @@
     private val communalColors: CommunalColors,
     private val ambientTouchComponentFactory: AmbientTouchComponent.Factory,
     private val communalContent: CommunalContent,
-    @Communal private val dataSourceDelegator: SceneDataSourceDelegator
+    @Communal private val dataSourceDelegator: SceneDataSourceDelegator,
+    private val notificationStackScrollLayoutController: NotificationStackScrollLayoutController,
 ) : LifecycleOwner {
+
+    private class CommunalWrapper(context: Context) : FrameLayout(context) {
+        private val consumers: MutableSet<Consumer<Boolean>> = ArraySet()
+
+        override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
+            consumers.forEach { it.accept(disallowIntercept) }
+            super.requestDisallowInterceptTouchEvent(disallowIntercept)
+        }
+
+        fun dispatchTouchEvent(
+            ev: MotionEvent?,
+            disallowInterceptConsumer: Consumer<Boolean>?
+        ): Boolean {
+            disallowInterceptConsumer?.apply { consumers.add(this) }
+
+            try {
+                return super.dispatchTouchEvent(ev)
+            } finally {
+                consumers.clear()
+            }
+        }
+    }
+
     /** The container view for the hub. This will not be initialized until [initView] is called. */
     private var communalContainerView: View? = null
 
+    /** Wrapper around the communal container to intercept touch events */
+    private var communalContainerWrapper: CommunalWrapper? = null
+
     /**
      * This lifecycle is used to control when the [touchMonitor] listens to touches. The lifecycle
      * should only be [Lifecycle.State.RESUMED] when the hub is showing and not covered by anything,
@@ -269,9 +303,13 @@
         )
         collectFlow(containerView, keyguardInteractor.isDreaming, { isDreaming = it })
 
-        communalContainerView = containerView
-
-        return containerView
+        if (glanceableHubFullscreenSwipe()) {
+            communalContainerWrapper = CommunalWrapper(containerView.context)
+            communalContainerWrapper?.addView(communalContainerView)
+            return communalContainerWrapper!!
+        } else {
+            return containerView
+        }
     }
 
     /**
@@ -304,6 +342,11 @@
             lifecycleRegistry.currentState = Lifecycle.State.CREATED
             communalContainerView = null
         }
+
+        communalContainerWrapper?.let {
+            (it.parent as ViewGroup).removeView(it)
+            communalContainerWrapper = null
+        }
     }
 
     /**
@@ -317,6 +360,18 @@
      */
     fun onTouchEvent(ev: MotionEvent): Boolean {
         SceneContainerFlag.assertInLegacyMode()
+
+        // In the case that we are handling full swipes on the lockscreen, are on the lockscreen,
+        // and the touch is within the horizontal notification band on the screen, do not process
+        // the touch.
+        if (
+            glanceableHubFullscreenSwipe() &&
+                !hubShowing &&
+                !notificationStackScrollLayoutController.isBelowLastNotification(ev.x, ev.y)
+        ) {
+            return false
+        }
+
         return communalContainerView?.let { handleTouchEventOnCommunalView(it, ev) } ?: false
     }
 
@@ -328,12 +383,16 @@
         val hubOccluded = anyBouncerShowing || shadeShowing
 
         if (isDown && !hubOccluded) {
-            val x = ev.rawX
-            val inOpeningSwipeRegion: Boolean = x >= view.width - rightEdgeSwipeRegionWidth
-            if (inOpeningSwipeRegion || hubShowing) {
-                // Steal touch events when the hub is open, or if the touch started in the opening
-                // gesture region.
+            if (glanceableHubFullscreenSwipe()) {
                 isTrackingHubTouch = true
+            } else {
+                val x = ev.rawX
+                val inOpeningSwipeRegion: Boolean = x >= view.width - rightEdgeSwipeRegionWidth
+                if (inOpeningSwipeRegion || hubShowing) {
+                    // Steal touch events when the hub is open, or if the touch started in the
+                    // opening gesture region.
+                    isTrackingHubTouch = true
+                }
             }
         }
 
@@ -341,10 +400,7 @@
             if (isUp || isCancel) {
                 isTrackingHubTouch = false
             }
-            dispatchTouchEvent(view, ev)
-            // Return true regardless of dispatch result as some touches at the start of a gesture
-            // may return false from dispatchTouchEvent.
-            return true
+            return dispatchTouchEvent(view, ev)
         }
 
         return false
@@ -354,13 +410,30 @@
      * Dispatches the touch event to the communal container and sends a user activity event to reset
      * the screen timeout.
      */
-    private fun dispatchTouchEvent(view: View, ev: MotionEvent) {
-        view.dispatchTouchEvent(ev)
-        powerManager.userActivity(
-            SystemClock.uptimeMillis(),
-            PowerManager.USER_ACTIVITY_EVENT_TOUCH,
-            0
-        )
+    private fun dispatchTouchEvent(view: View, ev: MotionEvent): Boolean {
+        try {
+            var handled = false
+            if (glanceableHubFullscreenSwipe()) {
+                communalContainerWrapper?.dispatchTouchEvent(ev) {
+                    if (it) {
+                        handled = true
+                    }
+                }
+                return handled || hubShowing
+            } else {
+                view.dispatchTouchEvent(ev)
+                // Return true regardless of dispatch result as some touches at the start of a
+                // gesture
+                // may return false from dispatchTouchEvent.
+                return true
+            }
+        } finally {
+            powerManager.userActivity(
+                SystemClock.uptimeMillis(),
+                PowerManager.USER_ACTIVITY_EVENT_TOUCH,
+                0
+            )
+        }
     }
 
     override val lifecycle: Lifecycle
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index 6df8ac4..4f6a64f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -65,6 +65,7 @@
 import com.android.systemui.DejankUtils;
 import com.android.systemui.Dumpable;
 import com.android.systemui.classifier.Classifier;
+import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
 import com.android.systemui.dump.DumpManager;
@@ -157,6 +158,7 @@
     private final ShadeRepository mShadeRepository;
     private final ShadeInteractor mShadeInteractor;
     private final ActiveNotificationsInteractor mActiveNotificationsInteractor;
+    private final Lazy<CommunalTransitionViewModel> mCommunalTransitionViewModelLazy;
     private final JavaAdapter mJavaAdapter;
     private final FalsingManager mFalsingManager;
     private final AccessibilityManager mAccessibilityManager;
@@ -334,6 +336,7 @@
             JavaAdapter javaAdapter,
             CastController castController,
             SplitShadeStateController splitShadeStateController,
+            Lazy<CommunalTransitionViewModel> communalTransitionViewModelLazy,
             Lazy<LargeScreenHeaderHelper> largeScreenHeaderHelperLazy
     ) {
         SceneContainerFlag.assertInLegacyMode();
@@ -379,6 +382,7 @@
         mShadeRepository = shadeRepository;
         mShadeInteractor = shadeInteractor;
         mActiveNotificationsInteractor = activeNotificationsInteractor;
+        mCommunalTransitionViewModelLazy = communalTransitionViewModelLazy;
         mJavaAdapter = javaAdapter;
 
         mLockscreenShadeTransitionController.addCallback(new LockscreenShadeTransitionCallback());
@@ -458,6 +462,9 @@
         initNotificationStackScrollLayoutController();
         mJavaAdapter.alwaysCollectFlow(
                 mShadeInteractor.isExpandToQsEnabled(), this::setExpansionEnabledPolicy);
+        mJavaAdapter.alwaysCollectFlow(
+                mCommunalTransitionViewModelLazy.get().isUmoOnCommunal(),
+                this::setShouldUpdateSquishinessOnMedia);
     }
 
     private void initNotificationStackScrollLayoutController() {
@@ -892,6 +899,12 @@
         }
     }
 
+    private void setShouldUpdateSquishinessOnMedia(boolean shouldUpdate) {
+        if (mQs != null) {
+            mQs.setShouldUpdateSquishinessOnMedia(shouldUpdate);
+        }
+    }
+
     void setOverScrollAmount(int overExpansion) {
         if (mQs != null) {
             mQs.setOverScrollAmount(overExpansion);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
index 5e4b173..a171d33 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java
@@ -381,7 +381,10 @@
         mLogger.logHandleUpdateCarrierInfo(info);
 
         mNoSimTextView.setVisibility(View.GONE);
-        if (!info.airplaneMode && info.anySimReady) {
+        if (info.isInSatelliteMode) {
+            mLogger.logUsingSatelliteText(info.carrierText);
+            showSingleText(info.carrierText);
+        } else if (!info.airplaneMode && info.anySimReady) {
             boolean[] slotSeen = new boolean[SIM_SLOTS];
             if (info.listOfCarriers.length == info.subscriptionIds.length) {
                 mLogger.logUsingSimViews();
@@ -416,22 +419,31 @@
                         info.listOfCarriers.length, info.subscriptionIds.length);
             }
         } else {
+            // No sims or airplane mode (but not WFC), so just show the main carrier text.
             mLogger.logUsingNoSimView(info.carrierText);
-            // No sims or airplane mode (but not WFC). Do not show ShadeCarrierGroup,
-            // instead just show info.carrierText in a different view.
-            for (int i = 0; i < SIM_SLOTS; i++) {
-                mInfos[i] = mInfos[i].changeVisibility(false);
-                mCarrierGroups[i].setCarrierText("");
-                mCarrierGroups[i].setVisibility(View.GONE);
-            }
-            mNoSimTextView.setText(info.carrierText);
-            if (!TextUtils.isEmpty(info.carrierText)) {
-                mNoSimTextView.setVisibility(View.VISIBLE);
-            }
+            showSingleText(info.carrierText);
         }
         handleUpdateState(); // handleUpdateCarrierInfo is always called from main thread.
     }
 
+    /**
+     * Shows only the given text in a single TextView and hides ShadeCarrierGroup (which would show
+     * individual SIM details).
+     */
+    private void showSingleText(CharSequence text) {
+        for (int i = 0; i < SIM_SLOTS; i++) {
+            mInfos[i] = mInfos[i].changeVisibility(false);
+            mCarrierGroups[i].setCarrierText("");
+            mCarrierGroups[i].setVisibility(View.GONE);
+        }
+        // TODO(b/341841138): Re-name this view now that it's being used for more than just the
+        //  no-SIM case.
+        mNoSimTextView.setText(text);
+        if (!TextUtils.isEmpty(text)) {
+            mNoSimTextView.setVisibility(View.VISIBLE);
+        }
+    }
+
     private static class H extends Handler {
         private Consumer<CarrierTextManager.CarrierTextCallbackInfo> mUpdateCarrierInfo;
         private Runnable mUpdateState;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerLogger.kt
index af06a35..b563cd9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerLogger.kt
@@ -65,6 +65,15 @@
         )
     }
 
+    fun logUsingSatelliteText(text: CharSequence) {
+        buffer.log(
+            TAG,
+            LogLevel.VERBOSE,
+            { str1 = "$text" },
+            { "┗ updating No SIM view with satellite text=$str1" },
+        )
+    }
+
     fun logUsingSimViews() {
         buffer.log(TAG, LogLevel.VERBOSE, {}, { "┗ updating SIM views" })
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
index 9885fe4..e7fc18e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorImpl.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.shade.domain.interactor
 
 import com.android.keyguard.LockIconViewController
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
@@ -30,7 +31,8 @@
 class ShadeLockscreenInteractorImpl
 @Inject
 constructor(
-    @Background private val scope: CoroutineScope,
+    @Application private val applicationScope: CoroutineScope,
+    @Background private val backgroundScope: CoroutineScope,
     private val shadeInteractor: ShadeInteractor,
     private val sceneInteractor: SceneInteractor,
     private val lockIconViewController: LockIconViewController,
@@ -68,7 +70,7 @@
         // Now handled elsewhere. Do nothing.
     }
     override fun transitionToExpandedShade(delay: Long) {
-        scope.launch {
+        backgroundScope.launch {
             delay(delay)
             changeToShadeScene()
         }
@@ -96,10 +98,12 @@
     }
 
     private fun changeToShadeScene() {
-        val shadeMode = shadeInteractor.shadeMode.value
-        sceneInteractor.changeScene(
-            if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade,
-            "ShadeLockscreenInteractorImpl.expandToNotifications",
-        )
+        applicationScope.launch {
+            val shadeMode = shadeInteractor.shadeMode.value
+            sceneInteractor.changeScene(
+                if (shadeMode is ShadeMode.Dual) Scenes.NotificationsShade else Scenes.Shade,
+                "ShadeLockscreenInteractorImpl.expandToNotifications",
+            )
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
index 95768e5..2dfc920 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
@@ -318,10 +318,10 @@
             };
 
             // Register to listen for changes in Settings.Secure settings.
-            mSecureSettings.registerContentObserverForUser(
+            mSecureSettings.registerContentObserverForUserSync(
                     Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS, mContentObserver,
                     UserHandle.USER_CURRENT);
-            mSecureSettings.registerContentObserverForUser(
+            mSecureSettings.registerContentObserverForUserSync(
                     Settings.Secure.USER_SETUP_COMPLETE, mContentObserver,
                     UserHandle.USER_CURRENT);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index 0d8030f..5268009 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -19,9 +19,14 @@
 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
 
+import static com.android.systemui.Flags.validateKeyboardShortcutHelperIconUri;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.app.AppGlobals;
+import android.app.SynchronousUserSwitchObserver;
+import android.app.UserSwitchObserver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -37,6 +42,7 @@
 import android.graphics.drawable.Icon;
 import android.hardware.input.InputManagerGlobal;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.text.Editable;
@@ -136,6 +142,8 @@
     };
 
     private final Handler mHandler = new Handler(Looper.getMainLooper());
+    private final HandlerThread mHandlerThread = new HandlerThread("KeyboardShortcutHelper");
+    @VisibleForTesting Handler mBackgroundHandler;
     @VisibleForTesting public Context mContext;
     private final IPackageManager mPackageManager;
 
@@ -143,6 +151,13 @@
     private KeyCharacterMap mKeyCharacterMap;
     private KeyCharacterMap mBackupKeyCharacterMap;
 
+    private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
+            @Override
+            public void onUserSwitching(int newUserId) throws RemoteException {
+                dismiss();
+            }
+    };
+
     @VisibleForTesting
     KeyboardShortcutListSearch(Context context, WindowManager windowManager) {
         this.mContext = new ContextThemeWrapper(
@@ -413,36 +428,75 @@
     private boolean mAppShortcutsReceived;
     private boolean mImeShortcutsReceived;
 
-    @VisibleForTesting
-    public void showKeyboardShortcuts(int deviceId) {
-        retrieveKeyCharacterMap(deviceId);
-        mAppShortcutsReceived = false;
-        mImeShortcutsReceived = false;
-        mWindowManager.requestAppKeyboardShortcuts(result -> {
-            // Add specific app shortcuts
+    private void onAppSpecificShortcutsReceived(List<KeyboardShortcutGroup> result) {
+        // Add specific app shortcuts
+        if (result != null) {
             if (result.isEmpty()) {
                 mCurrentAppPackageName = null;
                 mKeySearchResultMap.put(SHORTCUT_SPECIFICAPP_INDEX, false);
             } else {
                 mCurrentAppPackageName = result.get(0).getPackageName();
-                mSpecificAppGroup.addAll(reMapToKeyboardShortcutMultiMappingGroup(result));
+                if (validateKeyboardShortcutHelperIconUri()) {
+                    KeyboardShortcuts.sanitiseShortcuts(result);
+                }
+                mSpecificAppGroup.addAll(
+                        reMapToKeyboardShortcutMultiMappingGroup(result));
                 mKeySearchResultMap.put(SHORTCUT_SPECIFICAPP_INDEX, true);
             }
-            mAppShortcutsReceived = true;
-            if (mImeShortcutsReceived) {
-                mergeAndShowKeyboardShortcutsGroups();
-            }
-        }, deviceId);
-        mWindowManager.requestImeKeyboardShortcuts(result -> {
-            // Add specific Ime shortcuts
+        }
+        mAppShortcutsReceived = true;
+        if (mImeShortcutsReceived) {
+            mergeAndShowKeyboardShortcutsGroups();
+        }
+    }
+
+    private void onImeSpecificShortcutsReceived(List<KeyboardShortcutGroup> result) {
+        // Add specific Ime shortcuts
+        if (result != null) {
             if (!result.isEmpty()) {
-                mInputGroup.addAll(reMapToKeyboardShortcutMultiMappingGroup(result));
+                if (validateKeyboardShortcutHelperIconUri()) {
+                    KeyboardShortcuts.sanitiseShortcuts(result);
+                }
+                mInputGroup.addAll(
+                        reMapToKeyboardShortcutMultiMappingGroup(result));
             }
-            mImeShortcutsReceived = true;
-            if (mAppShortcutsReceived) {
-                mergeAndShowKeyboardShortcutsGroups();
+        }
+        mImeShortcutsReceived = true;
+        if (mAppShortcutsReceived) {
+            mergeAndShowKeyboardShortcutsGroups();
+        }
+    }
+
+    @VisibleForTesting
+    public void showKeyboardShortcuts(int deviceId) {
+        if (mBackgroundHandler == null) {
+            mHandlerThread.start();
+            mBackgroundHandler = new Handler(mHandlerThread.getLooper());
+        }
+
+        if (validateKeyboardShortcutHelperIconUri()) {
+            try {
+                ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
+            } catch (RemoteException e) {
+                Log.e(TAG, "could not register user switch observer", e);
             }
-        }, deviceId);
+        }
+
+        retrieveKeyCharacterMap(deviceId);
+        mAppShortcutsReceived = false;
+        mImeShortcutsReceived = false;
+        mWindowManager.requestAppKeyboardShortcuts(
+                result -> {
+                    mBackgroundHandler.post(() -> {
+                        onAppSpecificShortcutsReceived(result);
+                    });
+                }, deviceId);
+        mWindowManager.requestImeKeyboardShortcuts(
+                result -> {
+                    mBackgroundHandler.post(() -> {
+                        onImeSpecificShortcutsReceived(result);
+                    });
+                }, deviceId);
     }
 
     private void mergeAndShowKeyboardShortcutsGroups() {
@@ -508,6 +562,14 @@
             mKeyboardShortcutsBottomSheetDialog.dismiss();
             mKeyboardShortcutsBottomSheetDialog = null;
         }
+        mHandlerThread.quit();
+        if (validateKeyboardShortcutHelperIconUri()) {
+            try {
+                ActivityManager.getService().unregisterUserSwitchObserver(mUserSwitchObserver);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Could not unregister user switch observer", e);
+            }
+        }
     }
 
     private KeyboardShortcutMultiMappingGroup getMultiMappingSystemShortcuts(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index 21f608e..c742f641 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -20,11 +20,16 @@
 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
 
+import static com.android.systemui.Flags.validateKeyboardShortcutHelperIconUri;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.app.AlertDialog;
 import android.app.AppGlobals;
 import android.app.Dialog;
+import android.app.SynchronousUserSwitchObserver;
+import android.app.UserSwitchObserver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
@@ -39,6 +44,7 @@
 import android.graphics.drawable.Icon;
 import android.hardware.input.InputManager;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Log;
@@ -93,6 +99,8 @@
     };
 
     private final Handler mHandler = new Handler(Looper.getMainLooper());
+    private final HandlerThread mHandlerThread = new HandlerThread("KeyboardShortcutHelper");
+    @VisibleForTesting Handler mBackgroundHandler;
     @VisibleForTesting public Context mContext;
     private final IPackageManager mPackageManager;
     private final OnClickListener mDialogCloseListener = new DialogInterface.OnClickListener() {
@@ -129,6 +137,13 @@
     @Nullable private List<KeyboardShortcutGroup> mReceivedAppShortcutGroups = null;
     @Nullable private List<KeyboardShortcutGroup> mReceivedImeShortcutGroups = null;
 
+    private final UserSwitchObserver mUserSwitchObserver = new SynchronousUserSwitchObserver() {
+            @Override
+            public void onUserSwitching(int newUserId) throws RemoteException {
+                dismiss();
+            }
+    };
+
     @VisibleForTesting
     KeyboardShortcuts(Context context, WindowManager windowManager) {
         this.mContext = new ContextThemeWrapper(
@@ -374,21 +389,68 @@
 
     @VisibleForTesting
     public void showKeyboardShortcuts(int deviceId) {
+        if (mBackgroundHandler == null) {
+            mHandlerThread.start();
+            mBackgroundHandler = new Handler(mHandlerThread.getLooper());
+        }
+
+        if (validateKeyboardShortcutHelperIconUri()) {
+            try {
+                ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
+            } catch (RemoteException e) {
+                Log.e(TAG, "could not register user switch observer", e);
+            }
+        }
+
         retrieveKeyCharacterMap(deviceId);
+
         mReceivedAppShortcutGroups = null;
         mReceivedImeShortcutGroups = null;
+
         mWindowManager.requestAppKeyboardShortcuts(
                 result -> {
-                    mReceivedAppShortcutGroups = result;
-                    maybeMergeAndShowKeyboardShortcuts();
+                    mBackgroundHandler.post(() -> {
+                        onAppSpecificShortcutsReceived(result);
+                    });
                 }, deviceId);
         mWindowManager.requestImeKeyboardShortcuts(
                 result -> {
-                    mReceivedImeShortcutGroups = result;
-                    maybeMergeAndShowKeyboardShortcuts();
+                    mBackgroundHandler.post(() -> {
+                        onImeSpecificShortcutsReceived(result);
+                    });
                 }, deviceId);
     }
 
+    private void onAppSpecificShortcutsReceived(List<KeyboardShortcutGroup> result) {
+        mReceivedAppShortcutGroups =
+                result == null ? Collections.emptyList() : result;
+
+        if (validateKeyboardShortcutHelperIconUri()) {
+            sanitiseShortcuts(mReceivedAppShortcutGroups);
+        }
+
+        maybeMergeAndShowKeyboardShortcuts();
+    }
+
+    private void onImeSpecificShortcutsReceived(List<KeyboardShortcutGroup> result) {
+        mReceivedImeShortcutGroups =
+                result == null ? Collections.emptyList() : result;
+
+        if (validateKeyboardShortcutHelperIconUri()) {
+            sanitiseShortcuts(mReceivedImeShortcutGroups);
+        }
+
+        maybeMergeAndShowKeyboardShortcuts();
+    }
+
+    static void sanitiseShortcuts(List<KeyboardShortcutGroup> shortcutGroups) {
+        for (KeyboardShortcutGroup group : shortcutGroups) {
+            for (KeyboardShortcutInfo info : group.getItems()) {
+                info.clearIcon();
+            }
+        }
+    }
+
     private void maybeMergeAndShowKeyboardShortcuts() {
         if (mReceivedAppShortcutGroups == null || mReceivedImeShortcutGroups == null) {
             return;
@@ -413,6 +475,14 @@
             mKeyboardShortcutsDialog.dismiss();
             mKeyboardShortcutsDialog = null;
         }
+        mHandlerThread.quit();
+        if (validateKeyboardShortcutHelperIconUri()) {
+            try {
+                ActivityManager.getService().unregisterUserSwitchObserver(mUserSwitchObserver);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Could not unregister user switch observer", e);
+            }
+        }
     }
 
     private KeyboardShortcutGroup getSystemShortcuts() {
@@ -540,7 +610,7 @@
             keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo(
                     mContext.getString(R.string.keyboard_shortcut_group_applications_calendar),
                     calendarIcon,
-                    KeyEvent.KEYCODE_L,
+                    KeyEvent.KEYCODE_K,
                     KeyEvent.META_META_ON));
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
index 3cf61e2..8d3f728 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java
@@ -362,20 +362,34 @@
         }
 
         protected boolean hasSameIcon(Object parentData, Object childData) {
-            Icon parentIcon = ((Notification) parentData).getSmallIcon();
-            Icon childIcon = ((Notification) childData).getSmallIcon();
+            Icon parentIcon = getIcon((Notification) parentData);
+            Icon childIcon = getIcon((Notification) childData);
             return parentIcon.sameAs(childIcon);
         }
 
+        private static Icon getIcon(Notification notification) {
+            if (notification.shouldUseAppIcon()) {
+                return notification.getAppIcon();
+            }
+            return notification.getSmallIcon();
+        }
+
         /**
          * @return whether two ImageViews have the same colorFilterSet or none at all
          */
         protected boolean hasSameColor(Object parentData, Object childData) {
-            int parentColor = ((Notification) parentData).color;
-            int childColor = ((Notification) childData).color;
+            int parentColor = getColor((Notification) parentData);
+            int childColor = getColor((Notification) childData);
             return parentColor == childColor;
         }
 
+        private static int getColor(Notification notification) {
+            if (notification.shouldUseAppIcon()) {
+                return 0;  // the color filter isn't applied if using the app icon
+            }
+            return notification.color;
+        }
+
         @Override
         public boolean isEmpty(View view) {
             return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 854ef92..bb26f92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -16,6 +16,7 @@
 package com.android.systemui.statusbar;
 
 import static android.app.Flags.keyguardPrivateNotifications;
+import static android.app.Flags.redactSensitiveContentNotificationsOnLockscreen;
 import static android.app.StatusBarManager.ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED;
 import static android.app.StatusBarManager.EXTRA_KM_PRIVATE_NOTIFS_ALLOWED;
 import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
@@ -654,10 +655,12 @@
                 !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
         boolean isNotifForManagedProfile = mCurrentManagedProfiles.contains(userId);
         boolean isNotifUserRedacted = !userAllowsPrivateNotificationsInPublic(userId);
+        boolean isNotifSensitive = redactSensitiveContentNotificationsOnLockscreen()
+                && ent.getRanking() != null && ent.getRanking().hasSensitiveContent();
 
-        // redact notifications if the current user is redacting notifications; however if the
-        // notification is associated with a managed profile, we rely on the managed profile
-        // setting to determine whether to redact it
+        // redact notifications if the current user is redacting notifications or the notification
+        // contains sensitive content. However if the notification is associated with a managed
+        // profile, we rely on the managed profile setting to determine whether to redact it.
         boolean isNotifRedacted = (!isNotifForManagedProfile && isCurrentUserRedactingNotifs)
                 || isNotifUserRedacted;
 
@@ -666,10 +669,11 @@
         boolean userForcesRedaction = packageHasVisibilityOverride(ent.getSbn().getKey());
 
         if (keyguardPrivateNotifications()) {
-            return !mKeyguardAllowingNotifications
-                    || userForcesRedaction || notificationRequestsRedaction && isNotifRedacted;
+            return !mKeyguardAllowingNotifications || isNotifSensitive
+                    || userForcesRedaction || (notificationRequestsRedaction && isNotifRedacted);
         } else {
-            return userForcesRedaction || notificationRequestsRedaction && isNotifRedacted;
+            return userForcesRedaction || isNotifSensitive
+                    || (notificationRequestsRedaction && isNotifRedacted);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index 5bf2f41..6d34a0f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -15,7 +15,8 @@
  */
 package com.android.systemui.statusbar;
 
-import static com.android.systemui.Flags.mediaControlsUserInitiatedDismiss;
+import static com.android.systemui.Flags.mediaControlsUserInitiatedDeleteintent;
+import static com.android.systemui.Flags.notificationMediaManagerBackgroundExecution;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -26,12 +27,16 @@
 import android.media.session.MediaController;
 import android.media.session.MediaSession;
 import android.media.session.PlaybackState;
+import android.os.Handler;
 import android.service.notification.NotificationStats;
 import android.service.notification.StatusBarNotification;
 import android.util.Log;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.systemui.Dumpable;
 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.controls.domain.pipeline.MediaDataManager;
 import com.android.systemui.media.controls.shared.model.MediaData;
@@ -48,6 +53,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.Executor;
@@ -80,13 +86,16 @@
     private final ArrayList<MediaListener> mMediaListeners;
 
     private final Executor mBackgroundExecutor;
+    private final Handler mHandler;
 
     protected NotificationPresenter mPresenter;
-    private MediaController mMediaController;
+    @VisibleForTesting
+    MediaController mMediaController;
     private String mMediaNotificationKey;
     private MediaMetadata mMediaMetadata;
 
-    private final MediaController.Callback mMediaListener = new MediaController.Callback() {
+    @VisibleForTesting
+    final MediaController.Callback mMediaListener = new MediaController.Callback() {
         @Override
         public void onPlaybackStateChanged(PlaybackState state) {
             super.onPlaybackStateChanged(state);
@@ -107,11 +116,20 @@
             if (DEBUG_MEDIA) {
                 Log.v(TAG, "DEBUG_MEDIA: onMetadataChanged: " + metadata);
             }
-            mMediaMetadata = metadata;
+            if (notificationMediaManagerBackgroundExecution()) {
+                mBackgroundExecutor.execute(() -> setMediaMetadata(metadata));
+            } else {
+                setMediaMetadata(metadata);
+            }
+
             dispatchUpdateMediaMetaData();
         }
     };
 
+    private void setMediaMetadata(MediaMetadata metadata) {
+        mMediaMetadata = metadata;
+    }
+
     /**
      * Injected constructor. See {@link CentralSurfacesModule}.
      */
@@ -122,7 +140,9 @@
             NotifCollection notifCollection,
             MediaDataManager mediaDataManager,
             DumpManager dumpManager,
-            @Background Executor backgroundExecutor) {
+            @Background Executor backgroundExecutor,
+            @Main Handler handler
+    ) {
         mContext = context;
         mMediaListeners = new ArrayList<>();
         mVisibilityProvider = visibilityProvider;
@@ -130,6 +150,7 @@
         mNotifPipeline = notifPipeline;
         mNotifCollection = notifCollection;
         mBackgroundExecutor = backgroundExecutor;
+        mHandler = handler;
 
         setupNotifPipeline();
 
@@ -178,7 +199,7 @@
 
             @Override
             public void onMediaDataRemoved(@NonNull String key, boolean userInitiated) {
-                if (mediaControlsUserInitiatedDismiss() && !userInitiated) {
+                if (mediaControlsUserInitiatedDeleteintent() && !userInitiated) {
                     // Dismissing the notification will send the app's deleteIntent, so ignore if
                     // this was an automatic removal
                     Log.d(TAG, "Not dismissing " + key + " because it was removed by the system");
@@ -262,6 +283,14 @@
 
     public void addCallback(MediaListener callback) {
         mMediaListeners.add(callback);
+        if (notificationMediaManagerBackgroundExecution()) {
+            mBackgroundExecutor.execute(() -> updateMediaMetaData(callback));
+        } else {
+            updateMediaMetaData(callback);
+        }
+    }
+
+    private void updateMediaMetaData(MediaListener callback) {
         callback.onPrimaryMetadataOrStateChanged(mMediaMetadata,
                 getMediaControllerPlaybackState(mMediaController));
     }
@@ -273,7 +302,11 @@
     public void findAndUpdateMediaNotifications() {
         // TODO(b/169655907): get the semi-filtered notifications for current user
         Collection<NotificationEntry> allNotifications = mNotifPipeline.getAllNotifs();
-        findPlayingMediaNotification(allNotifications);
+        if (notificationMediaManagerBackgroundExecution()) {
+            mBackgroundExecutor.execute(() -> findPlayingMediaNotification(allNotifications));
+        } else {
+            findPlayingMediaNotification(allNotifications);
+        }
         dispatchUpdateMediaMetaData();
     }
 
@@ -312,7 +345,7 @@
             // We have a new media session
             clearCurrentMediaNotificationSession();
             mMediaController = controller;
-            mMediaController.registerCallback(mMediaListener);
+            mMediaController.registerCallback(mMediaListener, mHandler);
             mMediaMetadata = mMediaController.getMetadata();
             if (DEBUG_MEDIA) {
                 Log.v(TAG, "DEBUG_MEDIA: insert listener, found new controller: "
@@ -331,13 +364,29 @@
     }
 
     public void clearCurrentMediaNotification() {
+        if (notificationMediaManagerBackgroundExecution()) {
+            mBackgroundExecutor.execute(this::clearMediaNotification);
+        } else {
+            clearMediaNotification();
+        }
+    }
+
+    private void clearMediaNotification() {
         mMediaNotificationKey = null;
         clearCurrentMediaNotificationSession();
     }
 
     private void dispatchUpdateMediaMetaData() {
-        @PlaybackState.State int state = getMediaControllerPlaybackState(mMediaController);
         ArrayList<MediaListener> callbacks = new ArrayList<>(mMediaListeners);
+        if (notificationMediaManagerBackgroundExecution()) {
+            mBackgroundExecutor.execute(() -> updateMediaMetaData(callbacks));
+        } else {
+            updateMediaMetaData(callbacks);
+        }
+    }
+
+    private void updateMediaMetaData(List<MediaListener> callbacks) {
+        @PlaybackState.State int state = getMediaControllerPlaybackState(mMediaController);
         for (int i = 0; i < callbacks.size(); i++) {
             callbacks.get(i).onPrimaryMetadataOrStateChanged(mMediaMetadata, state);
         }
@@ -393,7 +442,6 @@
                 Log.v(TAG, "DEBUG_MEDIA: Disconnecting from old controller: "
                         + mMediaController.getPackageName());
             }
-            // TODO(b/336612071): move to background thread
             mMediaController.unregisterCallback(mMediaListener);
         }
         mMediaController = null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
new file mode 100644
index 0000000..ac16d26
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.mediaprojection.domain.interactor
+
+import com.android.systemui.common.shared.model.ContentDescription
+import com.android.systemui.common.shared.model.Icon
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.repository.MediaProjectionRepository
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor
+import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
+import com.android.systemui.util.time.SystemClock
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
+
+/**
+ * Interactor for media-projection-related chips in the status bar.
+ *
+ * There are two kinds of media projection events that will show chips in the status bar:
+ * 1) Share-to-app: Sharing your phone screen content to another app on the same device. (Triggered
+ *    from within each individual app.)
+ * 2) Cast-to-other-device: Sharing your phone screen content to a different device. (Triggered from
+ *    the Quick Settings Cast tile or from the Settings app.) This interactor handles both of those
+ *    event types (though maybe not audio-only casting -- see b/342169876).
+ */
+@SysUISingleton
+class MediaProjectionChipInteractor
+@Inject
+constructor(
+    @Application scope: CoroutineScope,
+    mediaProjectionRepository: MediaProjectionRepository,
+    val systemClock: SystemClock,
+) : OngoingActivityChipInteractor {
+    override val chip: StateFlow<OngoingActivityChipModel> =
+        mediaProjectionRepository.mediaProjectionState
+            .map { state ->
+                when (state) {
+                    is MediaProjectionState.NotProjecting -> OngoingActivityChipModel.Hidden
+                    is MediaProjectionState.EntireScreen,
+                    is MediaProjectionState.SingleTask -> {
+                        // TODO(b/332662551): Distinguish between cast-to-other-device and
+                        // share-to-app.
+                        OngoingActivityChipModel.Shown(
+                            icon =
+                                Icon.Resource(
+                                    R.drawable.ic_cast_connected,
+                                    ContentDescription.Resource(R.string.accessibility_casting)
+                                ),
+                            // TODO(b/332662551): See if we can use a MediaProjection API to fetch
+                            // this time.
+                            startTimeMs = systemClock.elapsedRealtime()
+                        ) {
+                            // TODO(b/332662551): Implement the pause dialog.
+                        }
+                    }
+                }
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
index bff5686..585ff5f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt
@@ -34,7 +34,7 @@
 
 /** Interactor for the screen recording chip shown in the status bar. */
 @SysUISingleton
-open class ScreenRecordChipInteractor
+class ScreenRecordChipInteractor
 @Inject
 constructor(
     @Application scope: CoroutineScope,
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
new file mode 100644
index 0000000..2032ec8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/ChipChronometerBinder.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.ui.binder
+
+import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
+
+object ChipChronometerBinder {
+    /**
+     * Updates the given [view] chronometer with a new start time and starts it.
+     *
+     * @param startTimeMs the time this event started, relative to
+     *   [com.android.systemui.util.time.SystemClock.elapsedRealtime]. See
+     *   [android.widget.Chronometer.setBase].
+     */
+    fun bind(startTimeMs: Long, view: ChipChronometer) {
+        view.base = startTimeMs
+        view.start()
+    }
+}
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 208eb50..edb2983 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
@@ -20,6 +20,7 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.statusbar.chips.call.domain.interactor.CallChipInteractor
 import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor
 import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -40,6 +41,7 @@
 constructor(
     @Application scope: CoroutineScope,
     screenRecordChipInteractor: ScreenRecordChipInteractor,
+    mediaProjectionChipInteractor: MediaProjectionChipInteractor,
     callChipInteractor: CallChipInteractor,
 ) {
 
@@ -51,10 +53,19 @@
      * actually displaying the chip.
      */
     val chip: StateFlow<OngoingActivityChipModel> =
-        combine(screenRecordChipInteractor.chip, callChipInteractor.chip) { screenRecord, call ->
+        combine(
+                screenRecordChipInteractor.chip,
+                mediaProjectionChipInteractor.chip,
+                callChipInteractor.chip
+            ) { screenRecord, mediaProjection, call ->
                 // This `when` statement shows the priority order of the chips
                 when {
+                    // Screen recording also activates the media projection APIs, so whenever the
+                    // screen recording chip is active, the media projection chip would also be
+                    // active. We want the screen-recording-specific chip shown in this case, so we
+                    // give the screen recording chip priority. See b/296461748.
                     screenRecord is OngoingActivityChipModel.Shown -> screenRecord
+                    mediaProjection is OngoingActivityChipModel.Shown -> mediaProjection
                     else -> call
                 }
             }
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 0524589..7df7ef1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -19,6 +19,7 @@
 import static com.android.systemui.Flags.predictiveBackAnimateDialogs;
 
 import android.content.Context;
+import android.os.Handler;
 import android.os.RemoteException;
 import android.service.dreams.IDreamManager;
 import android.util.Log;
@@ -99,7 +100,8 @@
             NotifCollection notifCollection,
             MediaDataManager mediaDataManager,
             DumpManager dumpManager,
-            @Background Executor backgroundExecutor) {
+            @Background Executor backgroundExecutor,
+            @Main Handler handler) {
         return new NotificationMediaManager(
                 context,
                 visibilityProvider,
@@ -107,7 +109,8 @@
                 notifCollection,
                 mediaDataManager,
                 dumpManager,
-                backgroundExecutor);
+                backgroundExecutor,
+                handler);
     }
 
     /** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 455c964..2e87a5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -143,6 +143,12 @@
     private var managedUserHandle: UserHandle? = null
     private var mSplitShadeEnabled = false
 
+    var suppressDisconnects = false
+        set(value) {
+            field = value
+            disconnect()
+        }
+
     // TODO(b/202758428): refactor so that we can test color updates via region samping, similar to
     //  how we test color updates when theme changes (See testThemeChangeUpdatesTextColor).
 
@@ -522,6 +528,7 @@
      */
     fun disconnect() {
         if (!smartspaceViews.isEmpty()) return
+        if (suppressDisconnects) return
 
         execution.assertIsMainThread()
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
index c643238..682a9ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ConversationNotifications.kt
@@ -34,8 +34,8 @@
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
-import com.android.systemui.statusbar.notification.row.NotificationContentInflaterLogger
 import com.android.systemui.statusbar.notification.row.NotificationContentView
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinderLogger
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator
 import com.android.systemui.statusbar.policy.HeadsUpManager
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener
@@ -44,30 +44,29 @@
 import javax.inject.Inject
 
 /** Populates additional information in conversation notifications */
-class ConversationNotificationProcessor @Inject constructor(
+class ConversationNotificationProcessor
+@Inject
+constructor(
     private val launcherApps: LauncherApps,
     private val conversationNotificationManager: ConversationNotificationManager
 ) {
     fun processNotification(
-            entry: NotificationEntry,
-            recoveredBuilder: Notification.Builder,
-            logger: NotificationContentInflaterLogger
+        entry: NotificationEntry,
+        recoveredBuilder: Notification.Builder,
+        logger: NotificationRowContentBinderLogger
     ): Notification.MessagingStyle? {
         val messagingStyle = recoveredBuilder.style as? Notification.MessagingStyle ?: return null
         messagingStyle.conversationType =
-                if (entry.ranking.channel.isImportantConversation)
-                    Notification.MessagingStyle.CONVERSATION_TYPE_IMPORTANT
-                else
-                    Notification.MessagingStyle.CONVERSATION_TYPE_NORMAL
+            if (entry.ranking.channel.isImportantConversation)
+                Notification.MessagingStyle.CONVERSATION_TYPE_IMPORTANT
+            else Notification.MessagingStyle.CONVERSATION_TYPE_NORMAL
         entry.ranking.conversationShortcutInfo?.let { shortcutInfo ->
             logger.logAsyncTaskProgress(entry, "getting shortcut icon")
             messagingStyle.shortcutIcon = launcherApps.getShortcutIcon(shortcutInfo)
-            shortcutInfo.label?.let { label ->
-                messagingStyle.conversationTitle = label
-            }
+            shortcutInfo.label?.let { label -> messagingStyle.conversationTitle = label }
         }
         messagingStyle.unreadMessageCount =
-                conversationNotificationManager.getUnreadCount(entry, recoveredBuilder)
+            conversationNotificationManager.getUnreadCount(entry, recoveredBuilder)
         return messagingStyle
     }
 }
@@ -77,7 +76,9 @@
  * animations to conserve CPU and memory.
  */
 @SysUISingleton
-class AnimatedImageNotificationManager @Inject constructor(
+class AnimatedImageNotificationManager
+@Inject
+constructor(
     private val notifCollection: CommonNotifCollection,
     private val bindEventManager: BindEventManager,
     private val headsUpManager: HeadsUpManager,
@@ -88,17 +89,21 @@
 
     /** Begins listening to state changes and updating animations accordingly. */
     fun bind() {
-        headsUpManager.addListener(object : OnHeadsUpChangedListener {
-            override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) {
-                updateAnimatedImageDrawables(entry)
+        headsUpManager.addListener(
+            object : OnHeadsUpChangedListener {
+                override fun onHeadsUpStateChanged(entry: NotificationEntry, isHeadsUp: Boolean) {
+                    updateAnimatedImageDrawables(entry)
+                }
             }
-        })
-        statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
-            override fun onExpandedChanged(isExpanded: Boolean) {
-                isStatusBarExpanded = isExpanded
-                notifCollection.allNotifs.forEach(::updateAnimatedImageDrawables)
+        )
+        statusBarStateController.addCallback(
+            object : StatusBarStateController.StateListener {
+                override fun onExpandedChanged(isExpanded: Boolean) {
+                    isStatusBarExpanded = isExpanded
+                    notifCollection.allNotifs.forEach(::updateAnimatedImageDrawables)
+                }
             }
-        })
+        )
         bindEventManager.addListener(::updateAnimatedImageDrawables)
     }
 
@@ -108,74 +113,73 @@
         }
 
     private fun updateAnimatedImageDrawables(row: ExpandableNotificationRow, animating: Boolean) =
-            (row.layouts?.asSequence() ?: emptySequence())
-                    .flatMap { layout -> layout.allViews.asSequence() }
-                    .flatMap { view ->
-                        (view as? ConversationLayout)?.messagingGroups?.asSequence()
-                                ?: (view as? MessagingLayout)?.messagingGroups?.asSequence()
-                                ?: emptySequence()
-                    }
-                    .flatMap { messagingGroup -> messagingGroup.messageContainer.children }
-                    .mapNotNull { view ->
-                        (view as? MessagingImageMessage)
-                                ?.let { imageMessage ->
-                                    imageMessage.drawable as? AnimatedImageDrawable
-                                }
-                    }
-                    .forEach { animatedImageDrawable ->
-                        if (animating) animatedImageDrawable.start()
-                        else animatedImageDrawable.stop()
-                    }
+        (row.layouts?.asSequence() ?: emptySequence())
+            .flatMap { layout -> layout.allViews.asSequence() }
+            .flatMap { view ->
+                (view as? ConversationLayout)?.messagingGroups?.asSequence()
+                    ?: (view as? MessagingLayout)?.messagingGroups?.asSequence() ?: emptySequence()
+            }
+            .flatMap { messagingGroup -> messagingGroup.messageContainer.children }
+            .mapNotNull { view ->
+                (view as? MessagingImageMessage)?.let { imageMessage ->
+                    imageMessage.drawable as? AnimatedImageDrawable
+                }
+            }
+            .forEach { animatedImageDrawable ->
+                if (animating) animatedImageDrawable.start() else animatedImageDrawable.stop()
+            }
 }
 
 /**
  * Tracks state related to conversation notifications, and updates the UI of existing notifications
  * when necessary.
+ *
  * TODO(b/214083332) Refactor this class to use the right coordinators and controllers
  */
 @SysUISingleton
-class ConversationNotificationManager @Inject constructor(
+class ConversationNotificationManager
+@Inject
+constructor(
     bindEventManager: BindEventManager,
     private val context: Context,
     private val notifCollection: CommonNotifCollection,
     @Main private val mainHandler: Handler
 ) {
     // Need this state to be thread safe, since it's accessed from the ui thread
-    // (NotificationEntryListener) and a bg thread (NotificationContentInflater)
+    // (NotificationEntryListener) and a bg thread (NotificationRowContentBinder)
     private val states = ConcurrentHashMap<String, ConversationState>()
 
     private var notifPanelCollapsed = true
 
     private fun updateNotificationRanking(rankingMap: RankingMap) {
         fun getLayouts(view: NotificationContentView) =
-                sequenceOf(view.contractedChild, view.expandedChild, view.headsUpChild)
+            sequenceOf(view.contractedChild, view.expandedChild, view.headsUpChild)
         val ranking = Ranking()
-        val activeConversationEntries = states.keys.asSequence()
-                .mapNotNull { notifCollection.getEntry(it) }
+        val activeConversationEntries =
+            states.keys.asSequence().mapNotNull { notifCollection.getEntry(it) }
         for (entry in activeConversationEntries) {
             if (rankingMap.getRanking(entry.sbn.key, ranking) && ranking.isConversation) {
                 val important = ranking.channel.isImportantConversation
                 var changed = false
-                entry.row?.layouts?.asSequence()
-                        ?.flatMap(::getLayouts)
-                        ?.mapNotNull { it as? ConversationLayout }
-                        ?.filterNot { it.isImportantConversation == important }
-                        ?.forEach { layout ->
-                            changed = true
-                            if (important && entry.isMarkedForUserTriggeredMovement) {
-                                // delay this so that it doesn't animate in until after
-                                // the notif has been moved in the shade
-                                mainHandler.postDelayed(
-                                        {
-                                            layout.setIsImportantConversation(
-                                                    important,
-                                                    true)
-                                        },
-                                        IMPORTANCE_ANIMATION_DELAY.toLong())
-                            } else {
-                                layout.setIsImportantConversation(important, false)
-                            }
+                entry.row
+                    ?.layouts
+                    ?.asSequence()
+                    ?.flatMap(::getLayouts)
+                    ?.mapNotNull { it as? ConversationLayout }
+                    ?.filterNot { it.isImportantConversation == important }
+                    ?.forEach { layout ->
+                        changed = true
+                        if (important && entry.isMarkedForUserTriggeredMovement) {
+                            // delay this so that it doesn't animate in until after
+                            // the notif has been moved in the shade
+                            mainHandler.postDelayed(
+                                { layout.setIsImportantConversation(important, true) },
+                                IMPORTANCE_ANIMATION_DELAY.toLong()
+                            )
+                        } else {
+                            layout.setIsImportantConversation(important, false)
                         }
+                    }
             }
         }
     }
@@ -192,9 +196,7 @@
         }
         entry.row?.setOnExpansionChangedListener { isExpanded ->
             if (entry.row?.isShown == true && isExpanded) {
-                entry.row.performOnIntrinsicHeightReached {
-                    updateCount(isExpanded)
-                }
+                entry.row.performOnIntrinsicHeightReached { updateCount(isExpanded) }
             } else {
                 updateCount(isExpanded)
             }
@@ -203,31 +205,38 @@
     }
 
     init {
-        notifCollection.addCollectionListener(object : NotifCollectionListener {
-            override fun onRankingUpdate(ranking: RankingMap) =
-                updateNotificationRanking(ranking)
+        notifCollection.addCollectionListener(
+            object : NotifCollectionListener {
+                override fun onRankingUpdate(ranking: RankingMap) =
+                    updateNotificationRanking(ranking)
 
-            override fun onEntryRemoved(entry: NotificationEntry, reason: Int) =
-                removeTrackedEntry(entry)
-        })
+                override fun onEntryRemoved(entry: NotificationEntry, reason: Int) =
+                    removeTrackedEntry(entry)
+            }
+        )
         bindEventManager.addListener(::onEntryViewBound)
     }
 
     private fun ConversationState.shouldIncrementUnread(newBuilder: Notification.Builder) =
-            if (notification.flags and Notification.FLAG_ONLY_ALERT_ONCE != 0) {
-                false
-            } else {
-                val oldBuilder = Notification.Builder.recoverBuilder(context, notification)
-                Notification.areStyledNotificationsVisiblyDifferent(oldBuilder, newBuilder)
-            }
+        if (notification.flags and Notification.FLAG_ONLY_ALERT_ONCE != 0) {
+            false
+        } else {
+            val oldBuilder = Notification.Builder.recoverBuilder(context, notification)
+            Notification.areStyledNotificationsVisiblyDifferent(oldBuilder, newBuilder)
+        }
 
     fun getUnreadCount(entry: NotificationEntry, recoveredBuilder: Notification.Builder): Int =
-            states.compute(entry.key) { _, state ->
-                val newCount = state?.run {
-                    if (shouldIncrementUnread(recoveredBuilder)) unreadCount + 1 else unreadCount
-                } ?: 1
+        states
+            .compute(entry.key) { _, state ->
+                val newCount =
+                    state?.run {
+                        if (shouldIncrementUnread(recoveredBuilder)) unreadCount + 1
+                        else unreadCount
+                    }
+                        ?: 1
                 ConversationState(newCount, entry.sbn.notification)
-            }!!.unreadCount
+            }!!
+            .unreadCount
 
     fun onNotificationPanelExpandStateChanged(isCollapsed: Boolean) {
         notifPanelCollapsed = isCollapsed
@@ -235,18 +244,17 @@
 
         // When the notification panel is expanded, reset the counters of any expanded
         // conversations
-        val expanded = states
+        val expanded =
+            states
                 .asSequence()
                 .mapNotNull { (key, _) ->
                     notifCollection.getEntry(key)?.let { entry ->
-                        if (entry.row?.isExpanded == true) key to entry
-                        else null
+                        if (entry.row?.isExpanded == true) key to entry else null
                     }
                 }
                 .toMap()
         states.replaceAll { key, state ->
-            if (expanded.contains(key)) state.copy(unreadCount = 0)
-            else state
+            if (expanded.contains(key)) state.copy(unreadCount = 0) else state
         }
         // Update UI separate from the replaceAll call, since ConcurrentHashMap may re-run the
         // lambda if threads are in contention.
@@ -262,16 +270,16 @@
     }
 
     private fun resetBadgeUi(row: ExpandableNotificationRow): Unit =
-            (row.layouts?.asSequence() ?: emptySequence())
-                    .flatMap { layout -> layout.allViews.asSequence() }
-                    .mapNotNull { view -> view as? ConversationLayout }
-                    .forEach { convoLayout -> convoLayout.setUnreadCount(0) }
+        (row.layouts?.asSequence() ?: emptySequence())
+            .flatMap { layout -> layout.allViews.asSequence() }
+            .mapNotNull { view -> view as? ConversationLayout }
+            .forEach { convoLayout -> convoLayout.setUnreadCount(0) }
 
     private data class ConversationState(val unreadCount: Int, val notification: Notification)
 
     companion object {
         private const val IMPORTANCE_ANIMATION_DELAY =
-                StackStateAnimator.ANIMATION_DURATION_STANDARD +
+            StackStateAnimator.ANIMATION_DURATION_STANDARD +
                 StackStateAnimator.ANIMATION_DURATION_PRIORITY_CHANGE +
                 100
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicChildBindController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicChildBindController.java
index 57b41f3..cafe6ffc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicChildBindController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicChildBindController.java
@@ -16,8 +16,8 @@
 
 package com.android.systemui.statusbar.notification;
 
-import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_CONTRACTED;
-import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_EXPANDED;
+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.stack.NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED;
 
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index 5bbd77e..60d846e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -23,6 +23,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
 import android.app.ActivityTaskManager.RootTaskInfo;
 import android.app.AppGlobals;
@@ -271,13 +272,16 @@
                     .addFlags(Intent.FLAG_IGNORE_EPHEMERAL)
                     .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 
+            ActivityOptions options = ActivityOptions.makeBasic()
+                    .setPendingIntentCreatorBackgroundActivityStartMode(
+                            ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
             PendingIntent pendingIntent =
                     PendingIntent.getActivityAsUser(
                             mContext,
                             0 /* requestCode */,
                             browserIntent,
                             PendingIntent.FLAG_IMMUTABLE /* flags */,
-                            null,
+                            options.toBundle(),
                             user);
             ComponentName aiaComponent = null;
             try {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
index 98109f9..fc47dc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
@@ -23,7 +23,7 @@
 import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
 import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
-import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
+import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder;
 
 import javax.inject.Inject;
 
@@ -100,9 +100,9 @@
         requireBinder().releaseViews(entry);
     }
 
-    private NotificationContentInflater.InflationCallback wrapInflationCallback(
+    private NotificationRowContentBinder.InflationCallback wrapInflationCallback(
             InflationCallback callback) {
-        return new NotificationContentInflater.InflationCallback() {
+        return new NotificationRowContentBinder.InflationCallback() {
             @Override
             public void handleInflationException(
                     NotificationEntry entry,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
index 42bf4e7..071192b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
@@ -289,8 +289,9 @@
             // for each change, lookup the new value
             .map {
                 secureSettings.getIntForUser(
-                    Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
-                    UserHandle.USER_CURRENT,
+                    name = Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
+                    def = 0,
+                    userHandle = UserHandle.USER_CURRENT,
                 ) == 1
             }
             // don't emit anything if nothing has changed
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
index 4c2ef83..4c82bc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
@@ -37,11 +37,13 @@
 import javax.inject.Inject
 
 /**
- * A class which provides an adjustment object to the preparation coordinator which is uses
- * to ensure that notifications are reinflated when ranking-derived information changes.
+ * A class which provides an adjustment object to the preparation coordinator which is uses to
+ * ensure that notifications are reinflated when ranking-derived information changes.
  */
 @SysUISingleton
-class NotifUiAdjustmentProvider @Inject constructor(
+class NotifUiAdjustmentProvider
+@Inject
+constructor(
     @Main private val handler: Handler,
     private val secureSettings: SecureSettings,
     private val lockscreenUserManager: NotificationLockscreenUserManager,
@@ -53,14 +55,13 @@
     private val dirtyListeners = ListenerSet<Runnable>()
     private var isSnoozeSettingsEnabled = false
 
-    /**
-     *  Update the snooze enabled value on user switch
-     */
-    private val userTrackerCallback = object : UserTracker.Callback {
-        override fun onUserChanged(newUser: Int, userContext: Context) {
-            updateSnoozeEnabled()
+    /** Update the snooze enabled value on user switch */
+    private val userTrackerCallback =
+        object : UserTracker.Callback {
+            override fun onUserChanged(newUser: Int, userContext: Context) {
+                updateSnoozeEnabled()
+            }
         }
-    }
 
     init {
         userTracker.addCallback(userTrackerCallback, HandlerExecutor(handler))
@@ -75,7 +76,7 @@
                 )
             }
             updateSnoozeEnabled()
-            secureSettings.registerContentObserverForUser(
+            secureSettings.registerContentObserverForUserSync(
                 SHOW_NOTIFICATION_SNOOZE,
                 settingsObserver,
                 UserHandle.USER_ALL
@@ -93,7 +94,7 @@
                     onSensitiveStateChangedListener
                 )
             }
-            secureSettings.unregisterContentObserver(settingsObserver)
+            secureSettings.unregisterContentObserverSync(settingsObserver)
         }
     }
 
@@ -104,12 +105,13 @@
 
     private val onSensitiveStateChangedListener = Runnable { dirtyListeners.forEach(Runnable::run) }
 
-    private val settingsObserver = object : ContentObserver(handler) {
-        override fun onChange(selfChange: Boolean) {
-            updateSnoozeEnabled()
-            dirtyListeners.forEach(Runnable::run)
+    private val settingsObserver =
+        object : ContentObserver(handler) {
+            override fun onChange(selfChange: Boolean) {
+                updateSnoozeEnabled()
+                dirtyListeners.forEach(Runnable::run)
+            }
         }
-    }
 
     private fun updateSnoozeEnabled() {
         isSnoozeSettingsEnabled =
@@ -126,22 +128,23 @@
     }
 
     /**
-     * Returns a adjustment object for the given entry.  This can be compared to a previous instance
+     * Returns a adjustment object for the given entry. This can be compared to a previous instance
      * from the same notification using [NotifUiAdjustment.needReinflate] to determine if it should
      * be reinflated.
      */
-    fun calculateAdjustment(entry: NotificationEntry) = NotifUiAdjustment(
-        key = entry.key,
-        smartActions = entry.ranking.smartActions,
-        smartReplies = entry.ranking.smartReplies,
-        isConversation = entry.ranking.isConversation,
-        isSnoozeEnabled = isSnoozeSettingsEnabled && !entry.isCanceled,
-        isMinimized = isEntryMinimized(entry),
-        needsRedaction =
-            lockscreenUserManager.needsRedaction(entry) ||
-                (screenshareNotificationHiding() &&
-                    sensitiveNotifProtectionController.shouldProtectNotification(entry)),
-        isChildInGroup = entry.hasEverBeenGroupChild(),
-        isGroupSummary = entry.hasEverBeenGroupSummary(),
-    )
+    fun calculateAdjustment(entry: NotificationEntry) =
+        NotifUiAdjustment(
+            key = entry.key,
+            smartActions = entry.ranking.smartActions,
+            smartReplies = entry.ranking.smartReplies,
+            isConversation = entry.ranking.isConversation,
+            isSnoozeEnabled = isSnoozeSettingsEnabled && !entry.isCanceled,
+            isMinimized = isEntryMinimized(entry),
+            needsRedaction =
+                lockscreenUserManager.needsRedaction(entry) ||
+                    (screenshareNotificationHiding() &&
+                        sensitiveNotifProtectionController.shouldProtectNotification(entry)),
+            isChildInGroup = entry.hasEverBeenGroupChild(),
+            isGroupSummary = entry.hasEverBeenGroupSummary(),
+        )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt
index 319b499..16d0cc4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconBuilder.kt
@@ -18,25 +18,27 @@
 
 import android.app.Notification
 import android.content.Context
+import android.graphics.drawable.Drawable
 import com.android.systemui.statusbar.StatusBarIconView
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.contentDescForNotification
 import javax.inject.Inject
 
-/**
- * Testable wrapper around Context.
- */
-class IconBuilder @Inject constructor(
-    private val context: Context
-) {
+/** Testable wrapper around Context. */
+class IconBuilder @Inject constructor(private val context: Context) {
     fun createIconView(entry: NotificationEntry): StatusBarIconView {
         return StatusBarIconView(
-                context,
-                "${entry.sbn.packageName}/0x${Integer.toHexString(entry.sbn.id)}",
-                entry.sbn)
+            context,
+            "${entry.sbn.packageName}/0x${Integer.toHexString(entry.sbn.id)}",
+            entry.sbn
+        )
     }
 
     fun getIconContentDescription(n: Notification): CharSequence {
         return contentDescForNotification(context, n)
     }
+
+    fun getAppIcon(n: Notification): Drawable {
+        return n.loadHeaderAppIcon(context)
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
index 271b0a8..3df9374 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconManager.kt
@@ -20,6 +20,8 @@
 import android.app.Notification.MessagingStyle
 import android.app.Person
 import android.content.pm.LauncherApps
+import android.graphics.drawable.AdaptiveIconDrawable
+import android.graphics.drawable.Drawable
 import android.graphics.drawable.Icon
 import android.os.Build
 import android.os.Bundle
@@ -165,7 +167,7 @@
                 Log.wtf(
                     TAG,
                     "Updating using the cache is not supported when the " +
-                        "notifications_background_conversation_icons flag is off"
+                        "notifications_background_icons flag is off"
                 )
             }
             if (!usingCache || !Flags.notificationsBackgroundIcons()) {
@@ -216,39 +218,85 @@
 
     @Throws(InflationException::class)
     private fun getIconDescriptor(entry: NotificationEntry, redact: Boolean): StatusBarIcon {
-        val n = entry.sbn.notification
         val showPeopleAvatar = !redact && isImportantConversation(entry)
 
+        // If the descriptor is already cached, return it
+        getCachedIconDescriptor(entry, showPeopleAvatar)?.also {
+            return it
+        }
+
+        val n = entry.sbn.notification
+        var usingMonochromeAppIcon = false
+        val icon: Icon?
+        if (showPeopleAvatar) {
+            icon = createPeopleAvatar(entry)
+        } else if (android.app.Flags.notificationsUseMonochromeAppIcon()) {
+            if (n.shouldUseAppIcon()) {
+                icon =
+                    getMonochromeAppIcon(entry)?.also { usingMonochromeAppIcon = true }
+                        ?: n.smallIcon
+            } else {
+                icon = n.smallIcon
+            }
+        } else {
+            icon = n.smallIcon
+        }
+
+        if (icon == null) {
+            throw InflationException("No icon in notification from ${entry.sbn.packageName}")
+        }
+
+        val sbi = icon.toStatusBarIcon(entry)
+        cacheIconDescriptor(entry, sbi, showPeopleAvatar, usingMonochromeAppIcon)
+        return sbi
+    }
+
+    private fun getCachedIconDescriptor(
+        entry: NotificationEntry,
+        showPeopleAvatar: Boolean
+    ): StatusBarIcon? {
         val peopleAvatarDescriptor = entry.icons.peopleAvatarDescriptor
+        val appIconDescriptor = entry.icons.appIconDescriptor
         val smallIconDescriptor = entry.icons.smallIconDescriptor
 
         // If cached, return corresponding cached values
-        if (showPeopleAvatar && peopleAvatarDescriptor != null) {
-            return peopleAvatarDescriptor
-        } else if (!showPeopleAvatar && smallIconDescriptor != null) {
-            return smallIconDescriptor
+        return when {
+            showPeopleAvatar && peopleAvatarDescriptor != null -> peopleAvatarDescriptor
+            android.app.Flags.notificationsUseMonochromeAppIcon() && appIconDescriptor != null ->
+                appIconDescriptor
+            smallIconDescriptor != null -> smallIconDescriptor
+            else -> null
         }
+    }
 
-        val icon =
-            (if (showPeopleAvatar) {
-                createPeopleAvatar(entry)
-            } else {
-                n.smallIcon
-            })
-                ?: throw InflationException("No icon in notification from " + entry.sbn.packageName)
-
-        val sbi = icon.toStatusBarIcon(entry)
-
-        // Cache if important conversation or app icon.
-        if (isImportantConversation(entry) || android.app.Flags.notificationsUseAppIcon()) {
+    private fun cacheIconDescriptor(
+        entry: NotificationEntry,
+        descriptor: StatusBarIcon,
+        showPeopleAvatar: Boolean,
+        usingMonochromeAppIcon: Boolean
+    ) {
+        if (android.app.Flags.notificationsUseAppIcon() ||
+            android.app.Flags.notificationsUseMonochromeAppIcon()
+        ) {
+            // If either of the new icon flags is enabled, we cache the icon all the time.
             if (showPeopleAvatar) {
-                entry.icons.peopleAvatarDescriptor = sbi
+                entry.icons.peopleAvatarDescriptor = descriptor
+            } else if (usingMonochromeAppIcon) {
+                // When notificationsUseMonochromeAppIcon is enabled, we use the appIconDescriptor.
+                entry.icons.appIconDescriptor = descriptor
             } else {
-                entry.icons.smallIconDescriptor = sbi
+                // When notificationsUseAppIcon is enabled, the app icon overrides the small icon.
+                // But either way, it's a good idea to cache the descriptor.
+                entry.icons.smallIconDescriptor = descriptor
+            }
+        } else if (isImportantConversation(entry)) {
+            // Old approach: cache only if important conversation.
+            if (showPeopleAvatar) {
+                entry.icons.peopleAvatarDescriptor = descriptor
+            } else {
+                entry.icons.smallIconDescriptor = descriptor
             }
         }
-
-        return sbi
     }
 
     @Throws(InflationException::class)
@@ -276,6 +324,29 @@
         )
     }
 
+    // TODO(b/335211019): Should we merge this with the method in GroupHelper?
+    private fun getMonochromeAppIcon(entry: NotificationEntry): Icon? {
+        // TODO(b/335211019): This should be done in the background.
+        var monochromeIcon: Icon? = null
+        try {
+            val appIcon: Drawable = iconBuilder.getAppIcon(entry.sbn.notification)
+            if (appIcon is AdaptiveIconDrawable) {
+                if (appIcon.monochrome != null) {
+                    monochromeIcon =
+                        Icon.createWithResourceAdaptiveDrawable(
+                            /* resPackage = */ entry.sbn.packageName,
+                            /* resId = */ appIcon.sourceDrawableResId,
+                            /* useMonochrome = */ true,
+                            /* inset = */ -3.0f * AdaptiveIconDrawable.getExtraInsetFraction()
+                        )
+                }
+            }
+        } catch (e: Exception) {
+            Log.e(TAG, "Failed to getAppIcon() in getMonochromeAppIcon()", e)
+        }
+        return monochromeIcon
+    }
+
     private suspend fun getLauncherShortcutIconForPeopleAvatar(entry: NotificationEntry) =
         withContext(bgCoroutineContext) {
             var icon: Icon? = null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java
index 442c097..d029ce7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/IconPack.java
@@ -33,6 +33,7 @@
     @Nullable private final StatusBarIconView mAodIcon;
 
     @Nullable private StatusBarIcon mSmallIconDescriptor;
+    @Nullable private StatusBarIcon mAppIconDescriptor;
     @Nullable private StatusBarIcon mPeopleAvatarDescriptor;
 
     private boolean mIsImportantConversation;
@@ -111,6 +112,15 @@
         mPeopleAvatarDescriptor = peopleAvatarDescriptor;
     }
 
+    @Nullable
+    StatusBarIcon getAppIconDescriptor() {
+        return mAppIconDescriptor;
+    }
+
+    void setAppIconDescriptor(@Nullable StatusBarIcon appIconDescriptor) {
+        mAppIconDescriptor = appIconDescriptor;
+    }
+
     boolean isImportantConversation() {
         return mIsImportantConversation;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt
index c74c396..c29d700 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/AvalancheProvider.kt
@@ -21,9 +21,9 @@
 import android.content.Intent
 import android.content.IntentFilter
 import android.util.Log
+import com.android.internal.logging.UiEventLogger
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
 
 // Class to track avalanche trigger event time.
@@ -33,6 +33,7 @@
 constructor(
         private val broadcastDispatcher: BroadcastDispatcher,
         private val logger: VisualInterruptionDecisionLogger,
+        private val uiEventLogger: UiEventLogger,
 ) {
     val TAG = "AvalancheProvider"
     val timeoutMs = 120000
@@ -56,6 +57,7 @@
                     return
                 }
                 Log.d(TAG, "broadcastReceiver received intent.action=" + intent.action)
+                uiEventLogger.log(AvalancheSuppressor.AvalancheEvent.START);
                 startTime = System.currentTimeMillis()
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
index 938a71f..f84b5f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
@@ -33,6 +33,8 @@
 import android.provider.Settings
 import android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED
 import android.provider.Settings.Global.HEADS_UP_OFF
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.settings.UserTracker
@@ -84,7 +86,7 @@
                 }
             }
 
-        globalSettings.registerContentObserver(
+        globalSettings.registerContentObserverSync(
             globalSettings.getUriFor(HEADS_UP_NOTIFICATIONS_ENABLED),
             /* notifyForDescendants = */ true,
             observer
@@ -92,7 +94,7 @@
 
         // QQQ: Do we need to register for SETTING_HEADS_UP_TICKER? It seems unused.
 
-        observer.onChange(/* selfChange = */ true)
+        observer.onChange(/* selfChange= */ true)
     }
 }
 
@@ -247,6 +249,7 @@
     private val systemClock: SystemClock,
     private val systemSettings: SystemSettings,
     private val packageManager: PackageManager,
+    private val uiEventLogger: UiEventLogger,
 ) :
     VisualInterruptionFilter(
         types = setOf(PEEK, PULSE),
@@ -266,6 +269,39 @@
         SUPPRESS
     }
 
+    enum class AvalancheEvent(private val id: Int) : UiEventLogger.UiEventEnum {
+        @UiEvent(
+            doc =
+                "An avalanche event occurred but this notification was suppressed by a " +
+                    "non-avalanche suppressor."
+        )
+        START(1802),
+        @UiEvent(doc = "HUN was suppressed in avalanche.") SUPPRESS(1803),
+        @UiEvent(doc = "HUN allowed during avalanche because it is high priority.")
+        ALLOW_CONVERSATION_AFTER_AVALANCHE(1804),
+        @UiEvent(doc = "HUN allowed during avalanche because it is a high priority conversation.")
+        ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME(1805),
+        @UiEvent(doc = "HUN allowed during avalanche because it is a call.") ALLOW_CALLSTYLE(1806),
+        @UiEvent(doc = "HUN allowed during avalanche because it is a calendar notification.")
+        ALLOW_CATEGORY_REMINDER(1807),
+        @UiEvent(doc = "HUN allowed during avalanche because it is a calendar notification.")
+        ALLOW_CATEGORY_EVENT(1808),
+        @UiEvent(
+            doc =
+                "HUN allowed during avalanche because it has a full screen intent and " +
+                    "the full screen intent permission is granted."
+        )
+        ALLOW_FSI_WITH_PERMISSION_ON(1809),
+        @UiEvent(doc = "HUN allowed during avalanche because it is colorized.")
+        ALLOW_COLORIZED(1810),
+        @UiEvent(doc = "HUN allowed during avalanche because it is an emergency notification.")
+        ALLOW_EMERGENCY(1811);
+
+        override fun getId(): Int {
+            return id
+        }
+    }
+
     override fun shouldSuppress(entry: NotificationEntry): Boolean {
         if (!isCooldownEnabled()) {
             return false
@@ -287,41 +323,46 @@
             entry.ranking.isConversation &&
                 entry.sbn.notification.getWhen() > avalancheProvider.startTime
         ) {
+            uiEventLogger.log(AvalancheEvent.ALLOW_CONVERSATION_AFTER_AVALANCHE)
             return State.ALLOW_CONVERSATION_AFTER_AVALANCHE
         }
 
         if (entry.channel?.isImportantConversation == true) {
+            uiEventLogger.log(AvalancheEvent.ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME)
             return State.ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME
         }
 
         if (entry.sbn.notification.isStyle(Notification.CallStyle::class.java)) {
+            uiEventLogger.log(AvalancheEvent.ALLOW_CALLSTYLE)
             return State.ALLOW_CALLSTYLE
         }
 
         if (entry.sbn.notification.category == CATEGORY_REMINDER) {
+            uiEventLogger.log(AvalancheEvent.ALLOW_CATEGORY_REMINDER)
             return State.ALLOW_CATEGORY_REMINDER
         }
 
         if (entry.sbn.notification.category == CATEGORY_EVENT) {
+            uiEventLogger.log(AvalancheEvent.ALLOW_CATEGORY_EVENT)
             return State.ALLOW_CATEGORY_EVENT
         }
 
         if (entry.sbn.notification.fullScreenIntent != null) {
+            uiEventLogger.log(AvalancheEvent.ALLOW_FSI_WITH_PERMISSION_ON)
             return State.ALLOW_FSI_WITH_PERMISSION_ON
         }
-
         if (entry.sbn.notification.isColorized) {
-            return State.ALLOW_COLORIZED
-        }
-        if (entry.sbn.notification.isColorized) {
+            uiEventLogger.log(AvalancheEvent.ALLOW_COLORIZED)
             return State.ALLOW_COLORIZED
         }
         if (
             packageManager.checkPermission(RECEIVE_EMERGENCY_BROADCAST, entry.sbn.packageName) ==
                 PERMISSION_GRANTED
         ) {
+            uiEventLogger.log(AvalancheEvent.ALLOW_EMERGENCY)
             return State.ALLOW_EMERGENCY
         }
+        uiEventLogger.log(AvalancheEvent.SUPPRESS)
         return State.SUPPRESS
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
index 332ece4..c7548aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
@@ -14,7 +14,6 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.NotificationLockscreenUserManager
@@ -41,8 +40,8 @@
 /** Determines if notifications should be visible based on the state of the keyguard. */
 interface KeyguardNotificationVisibilityProvider {
     /**
-     * Determines if the given notification should be hidden based on the current keyguard state.
-     * If a [Consumer] registered via [addOnStateChangedListener] is invoked, the results of this
+     * Determines if the given notification should be hidden based on the current keyguard state. If
+     * a [Consumer] registered via [addOnStateChangedListener] is invoked, the results of this
      * method may no longer be valid and should be re-queried.
      */
     fun shouldHideNotification(entry: NotificationEntry): Boolean
@@ -61,8 +60,9 @@
 @Module
 interface KeyguardNotificationVisibilityProviderImplModule {
     @Binds
-    fun bindImpl(impl: KeyguardNotificationVisibilityProviderImpl):
-            KeyguardNotificationVisibilityProvider
+    fun bindImpl(
+        impl: KeyguardNotificationVisibilityProviderImpl
+    ): KeyguardNotificationVisibilityProvider
 
     @Binds
     @IntoMap
@@ -71,7 +71,9 @@
 }
 
 @SysUISingleton
-class KeyguardNotificationVisibilityProviderImpl @Inject constructor(
+class KeyguardNotificationVisibilityProviderImpl
+@Inject
+constructor(
     @Main private val handler: Handler,
     private val keyguardStateController: KeyguardStateController,
     private val lockscreenUserManager: NotificationLockscreenUserManager,
@@ -84,76 +86,88 @@
     private val featureFlags: FeatureFlagsClassic
 ) : CoreStartable, KeyguardNotificationVisibilityProvider {
     private val showSilentNotifsUri =
-            secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS)
+        secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS)
     private val onStateChangedListeners = ListenerSet<Consumer<String>>()
     private var hideSilentNotificationsOnLockscreen: Boolean = false
 
-    private val userTrackerCallback = object : UserTracker.Callback {
-        override fun onUserChanged(newUser: Int, userContext: Context) {
-            readShowSilentNotificationSetting()
-            if (isLockedOrLocking) {
-                // maybe public mode changed
-                notifyStateChanged("onUserSwitched")
+    private val userTrackerCallback =
+        object : UserTracker.Callback {
+            override fun onUserChanged(newUser: Int, userContext: Context) {
+                readShowSilentNotificationSetting()
+                if (isLockedOrLocking) {
+                    // maybe public mode changed
+                    notifyStateChanged("onUserSwitched")
+                }
             }
         }
-    }
 
     override fun start() {
         readShowSilentNotificationSetting()
-        keyguardStateController.addCallback(object : KeyguardStateController.Callback {
-            override fun onUnlockedChanged() {
-                notifyStateChanged("onUnlockedChanged")
-            }
+        keyguardStateController.addCallback(
+            object : KeyguardStateController.Callback {
+                override fun onUnlockedChanged() {
+                    notifyStateChanged("onUnlockedChanged")
+                }
 
-            override fun onKeyguardShowingChanged() {
-                notifyStateChanged("onKeyguardShowingChanged")
+                override fun onKeyguardShowingChanged() {
+                    notifyStateChanged("onKeyguardShowingChanged")
+                }
             }
-        })
-        keyguardUpdateMonitor.registerCallback(object : KeyguardUpdateMonitorCallback() {
-            override fun onStrongAuthStateChanged(userId: Int) {
-                notifyStateChanged("onStrongAuthStateChanged")
+        )
+        keyguardUpdateMonitor.registerCallback(
+            object : KeyguardUpdateMonitorCallback() {
+                override fun onStrongAuthStateChanged(userId: Int) {
+                    notifyStateChanged("onStrongAuthStateChanged")
+                }
             }
-        })
+        )
 
         // register lockscreen settings changed callbacks:
-        val settingsObserver: ContentObserver = object : ContentObserver(handler) {
-            override fun onChange(selfChange: Boolean, uri: Uri?) {
-                if (uri == showSilentNotifsUri) {
-                    readShowSilentNotificationSetting()
-                }
-                if (isLockedOrLocking) {
-                    notifyStateChanged("Settings $uri changed")
+        val settingsObserver: ContentObserver =
+            object : ContentObserver(handler) {
+                override fun onChange(selfChange: Boolean, uri: Uri?) {
+                    if (uri == showSilentNotifsUri) {
+                        readShowSilentNotificationSetting()
+                    }
+                    if (isLockedOrLocking) {
+                        notifyStateChanged("Settings $uri changed")
+                    }
                 }
             }
-        }
 
-        secureSettings.registerContentObserverForUser(
-                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
-                settingsObserver,
-                UserHandle.USER_ALL)
+        secureSettings.registerContentObserverForUserSync(
+            Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+            settingsObserver,
+            UserHandle.USER_ALL
+        )
 
-        secureSettings.registerContentObserverForUser(
-                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
-                true,
-                settingsObserver,
-                UserHandle.USER_ALL)
+        secureSettings.registerContentObserverForUserSync(
+            Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+            true,
+            settingsObserver,
+            UserHandle.USER_ALL
+        )
 
-        globalSettings.registerContentObserver(Settings.Global.ZEN_MODE, settingsObserver)
+        globalSettings.registerContentObserverSync(Settings.Global.ZEN_MODE, settingsObserver)
 
-        secureSettings.registerContentObserverForUser(
-                Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS,
-                settingsObserver,
-                UserHandle.USER_ALL)
+        secureSettings.registerContentObserverForUserSync(
+            Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS,
+            settingsObserver,
+            UserHandle.USER_ALL
+        )
 
         // register (maybe) public mode changed callbacks:
-        statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
-            override fun onStateChanged(newState: Int) {
-                notifyStateChanged("onStatusBarStateChanged")
+        statusBarStateController.addCallback(
+            object : StatusBarStateController.StateListener {
+                override fun onStateChanged(newState: Int) {
+                    notifyStateChanged("onStatusBarStateChanged")
+                }
+
+                override fun onUpcomingStateChanged(state: Int) {
+                    notifyStateChanged("onStatusBarUpcomingStateChanged")
+                }
             }
-            override fun onUpcomingStateChanged(state: Int) {
-                notifyStateChanged("onStatusBarUpcomingStateChanged")
-            }
-        })
+        )
         userTracker.addCallback(userTrackerCallback, HandlerExecutor(handler))
     }
 
@@ -169,45 +183,48 @@
         onStateChangedListeners.forEach { it.accept(reason) }
     }
 
-    override fun shouldHideNotification(entry: NotificationEntry): Boolean = when {
-        // Keyguard state doesn't matter if the keyguard is not showing.
-        !isLockedOrLocking -> false
-        // Notifications not allowed on the lockscreen, always hide.
-        !lockscreenUserManager.shouldShowLockscreenNotifications() -> true
-        // User settings do not allow this notification on the lockscreen, so hide it.
-        userSettingsDisallowNotification(entry) -> true
-        // Entry is explicitly marked SECRET, so hide it.
-        entry.sbn.notification.visibility == VISIBILITY_SECRET -> true
-        // if entry is silent, apply custom logic to see if should hide
-        shouldHideIfEntrySilent(entry) -> true
-        else -> false
-    }
+    override fun shouldHideNotification(entry: NotificationEntry): Boolean =
+        when {
+            // Keyguard state doesn't matter if the keyguard is not showing.
+            !isLockedOrLocking -> false
+            // Notifications not allowed on the lockscreen, always hide.
+            !lockscreenUserManager.shouldShowLockscreenNotifications() -> true
+            // User settings do not allow this notification on the lockscreen, so hide it.
+            userSettingsDisallowNotification(entry) -> true
+            // Entry is explicitly marked SECRET, so hide it.
+            entry.sbn.notification.visibility == VISIBILITY_SECRET -> true
+            // if entry is silent, apply custom logic to see if should hide
+            shouldHideIfEntrySilent(entry) -> true
+            else -> false
+        }
 
-    private fun shouldHideIfEntrySilent(entry: ListEntry): Boolean = when {
-        // Show if explicitly high priority (not hidden)
-        highPriorityProvider.isExplicitlyHighPriority(entry) -> false
-        // Ambient notifications are hidden always from lock screen
-        entry.representativeEntry?.isAmbient == true -> true
-        // [Now notification is silent]
-        // Hide regardless of parent priority if user wants silent notifs hidden
-        hideSilentNotificationsOnLockscreen -> true
-        // Parent priority is high enough to be shown on the lockscreen, do not hide.
-        entry.parent?.let(::shouldHideIfEntrySilent) == false -> false
-        // Show when silent notifications are allowed on lockscreen
-        else -> false
-    }
+    private fun shouldHideIfEntrySilent(entry: ListEntry): Boolean =
+        when {
+            // Show if explicitly high priority (not hidden)
+            highPriorityProvider.isExplicitlyHighPriority(entry) -> false
+            // Ambient notifications are hidden always from lock screen
+            entry.representativeEntry?.isAmbient == true -> true
+            // [Now notification is silent]
+            // Hide regardless of parent priority if user wants silent notifs hidden
+            hideSilentNotificationsOnLockscreen -> true
+            // Parent priority is high enough to be shown on the lockscreen, do not hide.
+            entry.parent?.let(::shouldHideIfEntrySilent) == false -> false
+            // Show when silent notifications are allowed on lockscreen
+            else -> false
+        }
 
     private fun userSettingsDisallowNotification(entry: NotificationEntry): Boolean {
-        fun disallowForUser(user: Int) = when {
-            // user is in lockdown, always disallow
-            keyguardUpdateMonitor.isUserInLockdown(user) -> true
-            // device isn't public, no need to check public-related settings, so allow
-            !lockscreenUserManager.isLockscreenPublicMode(user) -> false
-            // entry is meant to be secret on the lockscreen, disallow
-            isRankingVisibilitySecret(entry) -> true
-            // disallow if user disallows notifications in public
-            else -> !lockscreenUserManager.userAllowsNotificationsInPublic(user)
-        }
+        fun disallowForUser(user: Int) =
+            when {
+                // user is in lockdown, always disallow
+                keyguardUpdateMonitor.isUserInLockdown(user) -> true
+                // device isn't public, no need to check public-related settings, so allow
+                !lockscreenUserManager.isLockscreenPublicMode(user) -> false
+                // entry is meant to be secret on the lockscreen, disallow
+                isRankingVisibilitySecret(entry) -> true
+                // disallow if user disallows notifications in public
+                else -> !lockscreenUserManager.userAllowsNotificationsInPublic(user)
+            }
         val currentUser = lockscreenUserManager.currentUserId
         val notifUser = entry.sbn.user.identifier
         return when {
@@ -222,28 +239,35 @@
         // ranking.lockscreenVisibilityOverride contains possibly out of date DPC and Setting
         // info, and NotificationLockscreenUserManagerImpl is already listening for updates
         // to those
-        return entry.ranking.channel != null && entry.ranking.channel.lockscreenVisibility ==
-                    VISIBILITY_SECRET
+        return entry.ranking.channel != null &&
+            entry.ranking.channel.lockscreenVisibility == VISIBILITY_SECRET
     }
 
-    override fun dump(pw: PrintWriter, args: Array<out String>) = pw.asIndenting().run {
-        println("isLockedOrLocking", isLockedOrLocking)
-        withIncreasedIndent {
-            println("keyguardStateController.isShowing", keyguardStateController.isShowing)
-            println("statusBarStateController.currentOrUpcomingState",
-                statusBarStateController.currentOrUpcomingState)
+    override fun dump(pw: PrintWriter, args: Array<out String>) =
+        pw.asIndenting().run {
+            println("isLockedOrLocking", isLockedOrLocking)
+            withIncreasedIndent {
+                println("keyguardStateController.isShowing", keyguardStateController.isShowing)
+                println(
+                    "statusBarStateController.currentOrUpcomingState",
+                    statusBarStateController.currentOrUpcomingState
+                )
+            }
+            println("hideSilentNotificationsOnLockscreen", hideSilentNotificationsOnLockscreen)
         }
-        println("hideSilentNotificationsOnLockscreen", hideSilentNotificationsOnLockscreen)
-    }
 
-    private val isLockedOrLocking get() =
-        keyguardStateController.isShowing ||
+    private val isLockedOrLocking
+        get() =
+            keyguardStateController.isShowing ||
                 statusBarStateController.currentOrUpcomingState == StatusBarState.KEYGUARD
 
     private fun readShowSilentNotificationSetting() {
         val showSilentNotifs =
-                secureSettings.getBoolForUser(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS,
-                        false, UserHandle.USER_CURRENT)
+            secureSettings.getBoolForUser(
+                Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS,
+                false,
+                UserHandle.USER_CURRENT
+            )
         hideSilentNotificationsOnLockscreen = !showSilentNotifs
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 74925c8..fea360d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -171,11 +171,11 @@
         };
 
         if (ENABLE_HEADS_UP) {
-            mGlobalSettings.registerContentObserver(
+            mGlobalSettings.registerContentObserverSync(
                     mGlobalSettings.getUriFor(HEADS_UP_NOTIFICATIONS_ENABLED),
                     true,
                     headsUpObserver);
-            mGlobalSettings.registerContentObserver(
+            mGlobalSettings.registerContentObserverSync(
                     mGlobalSettings.getUriFor(SETTING_HEADS_UP_TICKER), true,
                     headsUpObserver);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
index 7e16cd5..84f8662 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImpl.kt
@@ -178,7 +178,8 @@
 
         if (NotificationAvalancheSuppression.isEnabled) {
             addFilter(
-                AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager)
+                AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager,
+                        uiEventLogger)
             )
             avalancheProvider.register()
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMonitor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMonitor.kt
index 0fdf681..1efa56f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMonitor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryMonitor.kt
@@ -20,8 +20,6 @@
 import android.util.Log
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
 import dagger.Lazy
 import javax.inject.Inject
 
@@ -30,7 +28,6 @@
 class NotificationMemoryMonitor
 @Inject
 constructor(
-    private val featureFlags: FeatureFlags,
     private val notificationMemoryDumper: NotificationMemoryDumper,
     private val notificationMemoryLogger: Lazy<NotificationMemoryLogger>,
 ) : CoreStartable {
@@ -42,9 +39,6 @@
     override fun start() {
         Log.d(TAG, "NotificationMemoryMonitor initialized.")
         notificationMemoryDumper.init()
-        if (featureFlags.isEnabled(Flags.NOTIFICATION_MEMORY_LOGGING_ENABLED)) {
-            Log.d(TAG, "Notification memory logging enabled.")
-            notificationMemoryLogger.get().init()
-        }
+        notificationMemoryLogger.get().init()
     }
 }
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 edd2961..190a2cd 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
@@ -548,7 +548,7 @@
      * and ensuring that the view is freed when it is safe to remove.
      *
      * @param inflationFlag flag corresponding to the content view to be freed
-     * @deprecated By default, {@link NotificationContentInflater#unbindContent} will tell the
+     * @deprecated By default, {@link NotificationRowContentBinder#unbindContent} will tell the
      * view hierarchy to only free when the view is safe to remove so this method is no longer
      * needed. Will remove when all uses are gone.
      */
@@ -2843,14 +2843,10 @@
         mIsSystemChildExpanded = expanded;
     }
 
-    public void setLayoutListener(LayoutListener listener) {
+    public void setLayoutListener(@Nullable LayoutListener listener) {
         mLayoutListener = listener;
     }
 
-    public void removeListener() {
-        mLayoutListener = null;
-    }
-
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         Trace.beginSection(appendTraceStyleTag("ExpNotRow#onLayout"));
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 2f03871..6ba26d9 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
@@ -92,7 +92,7 @@
     private final NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider;
     private final HeadsUpStyleProvider mHeadsUpStyleProvider;
 
-    private final NotificationContentInflaterLogger mLogger;
+    private final NotificationRowContentBinderLogger mLogger;
 
     @Inject
     NotificationContentInflater(
@@ -104,7 +104,7 @@
             SmartReplyStateInflater smartRepliesInflater,
             NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider,
             HeadsUpStyleProvider headsUpStyleProvider,
-            NotificationContentInflaterLogger logger) {
+            NotificationRowContentBinderLogger logger) {
         mRemoteViewCache = remoteViewCache;
         mRemoteInputManager = remoteInputManager;
         mConversationProcessor = conversationProcessor;
@@ -345,7 +345,7 @@
             Context packageContext,
             InflatedSmartReplyState previousSmartReplyState,
             SmartReplyStateInflater inflater,
-            NotificationContentInflaterLogger logger) {
+            NotificationRowContentBinderLogger logger) {
         boolean inflateContracted = (reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0
                 && result.newContentView != null;
         boolean inflateExpanded = (reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0
@@ -377,7 +377,7 @@
             ExpandableNotificationRow row,
             NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider,
             HeadsUpStyleProvider headsUpStyleProvider,
-            NotificationContentInflaterLogger logger) {
+            NotificationRowContentBinderLogger logger) {
         return TraceUtils.trace("NotificationContentInflater.createRemoteViews", () -> {
             InflationProgress result = new InflationProgress();
             final NotificationEntry entryForLogging = row.getEntry();
@@ -465,7 +465,7 @@
             ExpandableNotificationRow row,
             RemoteViews.InteractionHandler remoteViewClickHandler,
             @Nullable InflationCallback callback,
-            NotificationContentInflaterLogger logger) {
+            NotificationRowContentBinderLogger logger) {
         Trace.beginAsyncSection(APPLY_TRACE_METHOD, System.identityHashCode(row));
 
         NotificationContentView privateLayout = row.getPrivateLayout();
@@ -676,7 +676,7 @@
             NotificationViewWrapper existingWrapper,
             final HashMap<Integer, CancellationSignal> runningInflations,
             ApplyCallback applyCallback,
-            NotificationContentInflaterLogger logger) {
+            NotificationRowContentBinderLogger logger) {
         RemoteViews newContentView = applyCallback.getRemoteView();
         if (inflateSynchronously) {
             try {
@@ -845,7 +845,7 @@
     private static void handleInflationError(
             HashMap<Integer, CancellationSignal> runningInflations, Exception e,
             NotificationEntry notification, @Nullable InflationCallback callback,
-            NotificationContentInflaterLogger logger, String logContext) {
+            NotificationRowContentBinderLogger logger, String logContext) {
         Assert.isMainThread();
         logger.logAsyncTaskException(notification, logContext, e);
         runningInflations.values().forEach(CancellationSignal::cancel);
@@ -864,7 +864,7 @@
             @InflationFlag int reInflateFlags, NotifRemoteViewCache remoteViewCache,
             HashMap<Integer, CancellationSignal> runningInflations,
             @Nullable InflationCallback endListener, NotificationEntry entry,
-            ExpandableNotificationRow row, NotificationContentInflaterLogger logger) {
+            ExpandableNotificationRow row, NotificationRowContentBinderLogger logger) {
         Assert.isMainThread();
         if (!runningInflations.isEmpty()) {
             return false;
@@ -1080,7 +1080,7 @@
         private final SmartReplyStateInflater mSmartRepliesInflater;
         private final NotifLayoutInflaterFactory.Provider mNotifLayoutInflaterFactoryProvider;
         private final HeadsUpStyleProvider mHeadsUpStyleProvider;
-        private final NotificationContentInflaterLogger mLogger;
+        private final NotificationRowContentBinderLogger mLogger;
 
         private AsyncInflationTask(
                 Executor inflationExecutor,
@@ -1099,7 +1099,7 @@
                 SmartReplyStateInflater smartRepliesInflater,
                 NotifLayoutInflaterFactory.Provider notifLayoutInflaterFactoryProvider,
                 HeadsUpStyleProvider headsUpStyleProvider,
-                NotificationContentInflaterLogger logger) {
+                NotificationRowContentBinderLogger logger) {
             mEntry = entry;
             mRow = row;
             mInflationExecutor = inflationExecutor;
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 c10c09c..bdfbc4b 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
@@ -242,7 +242,7 @@
     public void onLayout() {
         mIconsPlaced = false; // Force icons to be re-placed
         setMenuLocation();
-        mParent.removeListener();
+        mParent.setLayoutListener(null);
     }
 
     private void createMenuViews(boolean resetState) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
index 33339a7..c1302a0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinder.java
@@ -20,6 +20,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
+import androidx.annotation.VisibleForTesting;
+
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.lang.annotation.Retention;
@@ -72,6 +74,10 @@
             @NonNull ExpandableNotificationRow row,
             @InflationFlag int contentToUnbind);
 
+    /** For testing, ensure all inflation is synchronous. */
+    @VisibleForTesting
+    void setInflateSynchronously(boolean inflateSynchronously);
+
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = true,
             prefix = {"FLAG_CONTENT_VIEW_"},
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderLogger.kt
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterLogger.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderLogger.kt
index 15c7055..a32e1d7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderLogger.kt
@@ -32,7 +32,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag
 import javax.inject.Inject
 
-class NotificationContentInflaterLogger
+class NotificationRowContentBinderLogger
 @Inject
 constructor(@NotifInflationLog private val buffer: LogBuffer) {
     fun logNotBindingRowWasRemoved(entry: NotificationEntry) {
@@ -158,4 +158,4 @@
     }
 }
 
-private const val TAG = "NotificationContentInflater"
+private const val TAG = "NotificationRowContentBinder"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java
index a17c066..30dbfed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java
@@ -92,9 +92,9 @@
 
                 synchronized (mListeners) {
                     if (mListeners.size() > 0) {
-                        mSecureSettings.unregisterContentObserver(mContentObserver);
+                        mSecureSettings.unregisterContentObserverSync(mContentObserver);
                         for (Uri uri : mListeners.keySet()) {
-                            mSecureSettings.registerContentObserverForUser(
+                            mSecureSettings.registerContentObserverForUserSync(
                                     uri, false, mContentObserver, newUser);
                         }
                     }
@@ -131,7 +131,7 @@
             mListeners.put(uri, currentListeners);
             if (currentListeners.size() == 1) {
                 mBackgroundHandler.post(() -> {
-                    mSecureSettings.registerContentObserverForUser(
+                    mSecureSettings.registerContentObserverForUserSync(
                             uri, false, mContentObserver, mUserTracker.getUserId());
                 });
             }
@@ -159,7 +159,7 @@
 
             if (mListeners.size() == 0) {
                 mBackgroundHandler.post(() -> {
-                    mSecureSettings.unregisterContentObserver(mContentObserver);
+                    mSecureSettings.unregisterContentObserverSync(mContentObserver);
                 });
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
index bae89fb..427fb66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindParams.java
@@ -35,7 +35,7 @@
     /**
      * Content views that are out of date and need to be rebound.
      *
-     * TODO: This should go away once {@link NotificationContentInflater} is broken down into
+     * TODO: This should go away once {@link NotificationRowContentBinder} is broken down into
      * smaller stages as then the stage itself would be invalidated.
      */
     private @InflationFlag int mDirtyContentViews = mContentViews;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
index 3fce9ce..6fc82c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflater.kt
@@ -342,7 +342,7 @@
         reinflateFlags: Int,
         entry: NotificationEntry,
         context: Context,
-        logger: NotificationContentInflaterLogger,
+        logger: NotificationRowContentBinderLogger,
     ): HybridNotificationView? {
         if (AsyncHybridViewInflation.isUnexpectedlyInLegacyMode()) return null
         if (reinflateFlags and FLAG_CONTENT_VIEW_SINGLE_LINE == 0) {
@@ -354,7 +354,7 @@
 
         var view: HybridNotificationView? = null
 
-        traceSection("NotificationContentInflater#inflateSingleLineView") {
+        traceSection("SingleLineViewInflater#inflateSingleLineView") {
             val inflater = LayoutInflater.from(context)
             val layoutRes: Int =
                 if (isConversation)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControlsRefactorFlag.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NotificationRowContentBinderRefactor.kt
similarity index 81%
rename from packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControlsRefactorFlag.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NotificationRowContentBinderRefactor.kt
index 2850b4b..997acdd 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaControlsRefactorFlag.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/shared/NotificationRowContentBinderRefactor.kt
@@ -14,26 +14,26 @@
  * limitations under the License.
  */
 
-package com.android.systemui.media.controls.util
+package com.android.systemui.statusbar.notification.row.shared
 
 import com.android.systemui.Flags
 import com.android.systemui.flags.FlagToken
 import com.android.systemui.flags.RefactorFlagUtils
 
-/** Helper for reading or using the media_controls_refactor flag state. */
+/** Helper for reading or using the notification row content binder refactor flag state. */
 @Suppress("NOTHING_TO_INLINE")
-object MediaControlsRefactorFlag {
+object NotificationRowContentBinderRefactor {
     /** The aconfig flag name */
-    const val FLAG_NAME = Flags.FLAG_MEDIA_CONTROLS_REFACTOR
+    const val FLAG_NAME = Flags.FLAG_NOTIFICATION_ROW_CONTENT_BINDER_REFACTOR
 
     /** A token used for dependency declaration */
     val token: FlagToken
         get() = FlagToken(FLAG_NAME, isEnabled)
 
-    /** Is the flag enabled? */
+    /** Is the refactor enabled */
     @JvmStatic
     inline val isEnabled
-        get() = Flags.mediaControlsRefactor()
+        get() = Flags.notificationRowContentBinderRefactor()
 
     /**
      * Called to ensure code is only run when the flag is enabled. This protects users from the
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 bd7f766..d1fabb1 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
@@ -191,8 +191,12 @@
         updateTransformedTypes();
         addRemainingTransformTypes();
         updateCropToPaddingForImageViews();
-        Notification notification = row.getEntry().getSbn().getNotification();
-        mIcon.setTag(ImageTransformState.ICON_TAG, notification.getSmallIcon());
+        Notification n = row.getEntry().getSbn().getNotification();
+        if (n.shouldUseAppIcon()) {
+            mIcon.setTag(ImageTransformState.ICON_TAG, n.getAppIcon());
+        } else {
+            mIcon.setTag(ImageTransformState.ICON_TAG, n.getSmallIcon());
+        }
 
         // We need to reset all views that are no longer transforming in case a view was previously
         // transformed, but now we decided to transform its container instead.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationHeadsUpCycling.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationHeadsUpCycling.kt
index d6c73a9..2dccea6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationHeadsUpCycling.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationHeadsUpCycling.kt
@@ -16,24 +16,22 @@
 
 package com.android.systemui.statusbar.notification.shared
 
-import com.android.systemui.Flags
 import com.android.systemui.flags.FlagToken
-import com.android.systemui.flags.RefactorFlagUtils
 
 /** Helper for reading or using the heads-up cycling flag state. */
 @Suppress("NOTHING_TO_INLINE")
 object NotificationHeadsUpCycling {
-    /** The aconfig flag name - enable this feature when FLAG_NOTIFICATION_THROTTLE_HUN is on. */
-    const val FLAG_NAME = Flags.FLAG_NOTIFICATION_THROTTLE_HUN
+    /** The aconfig flag name */
+    const val FLAG_NAME = NotificationThrottleHun.FLAG_NAME
 
     /** A token used for dependency declaration */
     val token: FlagToken
-        get() = FlagToken(FLAG_NAME, isEnabled)
+        get() = NotificationThrottleHun.token
 
     /** Is the heads-up cycling animation enabled */
     @JvmStatic
     inline val isEnabled
-        get() = Flags.notificationThrottleHun()
+        get() = NotificationThrottleHun.isEnabled
 
     /** Whether to animate the bottom line when transiting from a tall HUN to a short HUN */
     @JvmStatic
@@ -46,13 +44,12 @@
      * build to ensure that the refactor author catches issues in testing.
      */
     @JvmStatic
-    inline fun isUnexpectedlyInLegacyMode() =
-        RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
+    inline fun isUnexpectedlyInLegacyMode() = NotificationThrottleHun.isUnexpectedlyInLegacyMode()
 
     /**
      * Called to ensure code is only run when the flag is disabled. This will throw an exception if
      * the flag is enabled to ensure that the refactor author catches issues in testing.
      */
     @JvmStatic
-    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
+    inline fun assertInLegacyMode() = NotificationThrottleHun.assertInLegacyMode()
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationThrottleHun.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationThrottleHun.kt
index dd81d42..71f0de0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationThrottleHun.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/NotificationThrottleHun.kt
@@ -24,7 +24,7 @@
 @Suppress("NOTHING_TO_INLINE")
 object NotificationThrottleHun {
     /** The aconfig flag name */
-    const val FLAG_NAME = Flags.FLAG_NOTIFICATION_THROTTLE_HUN
+    const val FLAG_NAME = Flags.FLAG_NOTIFICATION_AVALANCHE_THROTTLE_HUN
 
     /** A token used for dependency declaration */
     val token: FlagToken
@@ -33,7 +33,7 @@
     /** Is the refactor enabled */
     @JvmStatic
     inline val isEnabled
-        get() = Flags.notificationThrottleHun()
+        get() = Flags.notificationAvalancheThrottleHun()
 
     /**
      * Called to ensure code is only run when the flag is enabled. This protects users from the
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 a9d7cc0..fe22cc6 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
@@ -24,7 +24,6 @@
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_SHADE_CLEAR_ALL;
 import static com.android.systemui.Flags.newAodTransition;
 import static com.android.systemui.Flags.notificationOverExpansionClippingFix;
-import static com.android.systemui.flags.Flags.UNCLEARED_TRANSIENT_HUN_FIX;
 import static com.android.systemui.statusbar.notification.stack.NotificationPriorityBucketKt.BUCKET_SILENT;
 import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_SWIPE;
 import static com.android.systemui.util.DumpUtilsKt.println;
@@ -255,7 +254,8 @@
      * The raw amount of the overScroll on the bottom, which is not rubber-banded.
      */
     private float mOverScrolledBottomPixels;
-    private ListenerSet<Runnable> mStackHeightChangedListeners = new ListenerSet<>();
+    private final ListenerSet<Runnable> mStackHeightChangedListeners = new ListenerSet<>();
+    private final ListenerSet<Runnable> mHeadsUpHeightChangedListeners = new ListenerSet<>();
     private NotificationLogger.OnChildLocationsChangedListener mListener;
     private OnNotificationLocationsChangedListener mLocationsChangedListener;
     private OnOverscrollTopChangedListener mOverscrollTopChangedListener;
@@ -1114,6 +1114,28 @@
         mStackHeightChangedListeners.remove(runnable);
     }
 
+    private void notifyHeadsUpHeightChangedForView(View view) {
+        if (mTopHeadsUpRow == view) {
+            notifyHeadsUpHeightChangedListeners();
+        }
+    }
+
+    private void notifyHeadsUpHeightChangedListeners() {
+        for (Runnable listener : mHeadsUpHeightChangedListeners) {
+            listener.run();
+        }
+    }
+
+    @Override
+    public void addHeadsUpHeightChangedListener(@NonNull Runnable runnable) {
+        mHeadsUpHeightChangedListeners.addIfAbsent(runnable);
+    }
+
+    @Override
+    public void removeHeadsUpHeightChangedListener(@NonNull Runnable runnable) {
+        mHeadsUpHeightChangedListeners.remove(runnable);
+    }
+
     @Override
     protected void onLayout(boolean changed, int l, int t, int r, int b) {
         if (!mSuppressChildrenMeasureAndLayout) {
@@ -2444,6 +2466,11 @@
         return mScrollViewFields.getIntrinsicStackHeight();
     }
 
+    @Override
+    public int getTopHeadsUpHeight() {
+        return getTopHeadsUpPinnedHeight();
+    }
+
     /**
      * Calculate the gap height between two different views
      *
@@ -2816,23 +2843,15 @@
             mAddedHeadsUpChildren.remove(child);
             return false;
         }
-        if (mFeatureFlags.isEnabled(UNCLEARED_TRANSIENT_HUN_FIX)) {
-            // Skip adding animation for clicked heads up notifications when the
-            // Shade is closed, because the animation event is generated in
-            // generateHeadsUpAnimationEvents. Only report that an animation was
-            // actually generated (thus requesting the transient view be added)
-            // if a removal animation is in progress.
-            if (!isExpanded() && isClickedHeadsUp(child)) {
-                // An animation is already running, add it transiently
-                mClearTransientViewsWhenFinished.add(child);
-                return child.inRemovalAnimation();
-            }
-        } else {
-            if (isClickedHeadsUp(child)) {
-                // An animation is already running, add it transiently
-                mClearTransientViewsWhenFinished.add(child);
-                return true;
-            }
+        // Skip adding animation for clicked heads up notifications when the
+        // Shade is closed, because the animation event is generated in
+        // generateHeadsUpAnimationEvents. Only report that an animation was
+        // actually generated (thus requesting the transient view be added)
+        // if a removal animation is in progress.
+        if (!isExpanded() && isClickedHeadsUp(child)) {
+            // An animation is already running, add it transiently
+            mClearTransientViewsWhenFinished.add(child);
+            return child.inRemovalAnimation();
         }
         if (mDebugRemoveAnimation) {
             Log.d(TAG, "generateRemove " + key
@@ -4193,12 +4212,14 @@
             requestAnimationOnViewResize(row);
         }
         requestChildrenUpdate();
+        notifyHeadsUpHeightChangedForView(view);
         mAnimateStackYForContentHeightChange = previouslyNeededAnimation;
     }
 
     void onChildHeightReset(ExpandableView view) {
         updateAnimationState(view);
         updateChronometerForChild(view);
+        notifyHeadsUpHeightChangedForView(view);
     }
 
     private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
@@ -5573,6 +5594,7 @@
      */
     public void setTopHeadsUpRow(@Nullable ExpandableNotificationRow topHeadsUpRow) {
         mTopHeadsUpRow = topHeadsUpRow;
+        notifyHeadsUpHeightChangedListeners();
     }
 
     public boolean getIsExpanded() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
index db544ce..f6722a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationPlaceholderRepository.kt
@@ -38,9 +38,6 @@
      */
     val shadeScrimBounds = MutableStateFlow<ShadeScrimBounds?>(null)
 
-    /** the y position of the top of the HUN area */
-    val headsUpTop = MutableStateFlow(0f)
-
     /** height made available to the notifications in the size-constrained mode of lock screen. */
     val constrainedAvailableSpace = MutableStateFlow(0)
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
index 463c631..f6d9351 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/data/repository/NotificationViewHeightRepository.kt
@@ -27,9 +27,6 @@
 @SysUISingleton
 class NotificationViewHeightRepository @Inject constructor() {
 
-    /** The height in px of the current heads up notification. */
-    val headsUpHeight = MutableStateFlow(0f)
-
     /**
      * The amount in px that the notification stack should scroll due to internal expansion. This
      * should only happen when a notification expansion hits the bottom of the screen, so it is
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
index e7acbe3..8557afc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/NotificationStackAppearanceInteractor.kt
@@ -65,9 +65,6 @@
             }
             .distinctUntilChanged()
 
-    /** The height in px of the contents of the HUN. */
-    val headsUpHeight: StateFlow<Float> = viewHeightRepository.headsUpHeight.asStateFlow()
-
     /** The alpha of the Notification Stack for the brightness mirror */
     val alphaForBrightnessMirror: StateFlow<Float> =
         placeholderRepository.alphaForBrightnessMirror.asStateFlow()
@@ -82,9 +79,6 @@
      */
     val scrolledToTop: StateFlow<Boolean> = placeholderRepository.scrolledToTop.asStateFlow()
 
-    /** The y-coordinate in px of bottom of the contents of the HUN. */
-    val headsUpTop: StateFlow<Float> = placeholderRepository.headsUpTop.asStateFlow()
-
     /**
      * The amount in px that the notification stack should scroll due to internal expansion. This
      * should only happen when a notification expansion hits the bottom of the screen, so it is
@@ -110,11 +104,6 @@
         placeholderRepository.shadeScrimBounds.value = bounds
     }
 
-    /** Sets the height of heads up notification. */
-    fun setHeadsUpHeight(height: Float) {
-        viewHeightRepository.headsUpHeight.value = height
-    }
-
     /** Sets whether the notification stack is scrolled to the top. */
     fun setScrolledToTop(scrolledToTop: Boolean) {
         placeholderRepository.scrolledToTop.value = scrolledToTop
@@ -133,8 +122,4 @@
     fun setConstrainedAvailableSpace(height: Int) {
         placeholderRepository.constrainedAvailableSpace.value = height
     }
-
-    fun setHeadsUpTop(headsUpTop: Float) {
-        placeholderRepository.headsUpTop.value = headsUpTop
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
index 14b882f..eaaa9a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationScrollView.kt
@@ -32,6 +32,9 @@
      */
     val intrinsicStackHeight: Int
 
+    /** Height in pixels required to display the top HeadsUp Notification. */
+    val topHeadsUpHeight: Int
+
     /**
      * Since this is an interface rather than a literal View, this provides cast-like access to the
      * underlying view.
@@ -72,9 +75,18 @@
     /** Sets whether the view is displayed in doze mode. */
     fun setDozing(dozing: Boolean)
 
-    /** Sets a listener to be notified, when the stack height might have changed. */
+    /** Adds a listener to be notified, when the stack height might have changed. */
     fun addStackHeightChangedListener(runnable: Runnable)
 
     /** @see addStackHeightChangedListener */
     fun removeStackHeightChangedListener(runnable: Runnable)
+
+    /**
+     * Adds a listener to be notified, when the height of the top heads up notification might have
+     * changed.
+     */
+    fun addHeadsUpHeightChangedListener(runnable: Runnable)
+
+    /** @see addHeadsUpHeightChangedListener */
+    fun removeHeadsUpHeightChangedListener(runnable: Runnable)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
index 622d8e7..fd08e89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationScrollViewBinder.kt
@@ -80,7 +80,6 @@
 
         launch { viewModel.maxAlpha.collect { view.setMaxAlpha(it) } }
         launch { viewModel.scrolledToTop.collect { view.setScrolledToTop(it) } }
-        launch { viewModel.headsUpTop.collect { view.setHeadsUpTop(it) } }
         launch { viewModel.expandFraction.collect { view.setExpandFraction(it.coerceIn(0f, 1f)) } }
         launch { viewModel.isScrollable.collect { view.setScrollingEnabled(it) } }
         launch { viewModel.isDozing.collect { isDozing -> view.setDozing(isDozing) } }
@@ -88,11 +87,9 @@
         launchAndDispose {
             view.setSyntheticScrollConsumer(viewModel.syntheticScrollConsumer)
             view.setCurrentGestureOverscrollConsumer(viewModel.currentGestureOverscrollConsumer)
-            view.setHeadsUpHeightConsumer(viewModel.headsUpHeightConsumer)
             DisposableHandle {
                 view.setSyntheticScrollConsumer(null)
                 view.setCurrentGestureOverscrollConsumer(null)
-                view.setHeadsUpHeightConsumer(null)
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
index c2ce114..e90a64a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt
@@ -139,8 +139,6 @@
      */
     val scrolledToTop: Flow<Boolean> =
         stackAppearanceInteractor.scrolledToTop.dumpValue("scrolledToTop")
-    /** The y-coordinate in px of bottom of the contents of the HUN. */
-    val headsUpTop: Flow<Float> = stackAppearanceInteractor.headsUpTop.dumpValue("headsUpTop")
 
     /** Receives the amount (px) that the stack should scroll due to internal expansion. */
     val syntheticScrollConsumer: (Float) -> Unit = stackAppearanceInteractor::setSyntheticScroll
@@ -150,8 +148,6 @@
      */
     val currentGestureOverscrollConsumer: (Boolean) -> Unit =
         stackAppearanceInteractor::setCurrentGestureOverscroll
-    /** Receives the height of the heads up notification. */
-    val headsUpHeightConsumer: (Float) -> Unit = stackAppearanceInteractor::setHeadsUpHeight
 
     /** Whether the notification stack is scrollable or not. */
     val isScrollable: Flow<Boolean> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
index 97b86e3..634bd7e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationsPlaceholderViewModel.kt
@@ -29,7 +29,6 @@
 import com.android.systemui.util.kotlin.FlowDumperImpl
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.StateFlow
 
 /**
  * ViewModel used by the Notification placeholders inside the scene container to update the
@@ -61,10 +60,6 @@
         interactor.setConstrainedAvailableSpace(height)
     }
 
-    fun onHeadsUpTopChanged(headsUpTop: Float) {
-        interactor.setHeadsUpTop(headsUpTop)
-    }
-
     /** Sets the content alpha for the current state of the brightness mirror */
     fun setAlphaForBrightnessMirror(alpha: Float) {
         interactor.setAlphaForBrightnessMirror(alpha)
@@ -74,9 +69,6 @@
     val shadeScrimRounding: Flow<ShadeScrimRounding> =
         interactor.shadeScrimRounding.dumpWhileCollecting("shadeScrimRounding")
 
-    /** The height in px of the contents of the HUN. */
-    val headsUpHeight: StateFlow<Float> = interactor.headsUpHeight.dumpValue("headsUpHeight")
-
     /**
      * The amount [0-1] that the shade or quick settings has been opened. At 0, the shade is closed;
      * at 1, either the shade or quick settings is open.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
index 6546db9..2ab7aa9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt
@@ -42,7 +42,10 @@
     private val activityStarterInternal: ActivityStarterInternal = legacyActivityStarter.get()
 
     override fun startPendingIntentDismissingKeyguard(intent: PendingIntent) {
-        activityStarterInternal.startPendingIntentDismissingKeyguard(intent = intent)
+        activityStarterInternal.startPendingIntentDismissingKeyguard(
+            intent = intent,
+            dismissShade = true
+        )
     }
 
     override fun startPendingIntentDismissingKeyguard(
@@ -52,6 +55,7 @@
         activityStarterInternal.startPendingIntentDismissingKeyguard(
             intent = intent,
             intentSentUiThreadCallback = intentSentUiThreadCallback,
+            dismissShade = true,
         )
     }
 
@@ -64,6 +68,7 @@
             intent = intent,
             intentSentUiThreadCallback = intentSentUiThreadCallback,
             associatedView = associatedView,
+            dismissShade = true,
         )
     }
 
@@ -76,6 +81,7 @@
             intent = intent,
             intentSentUiThreadCallback = intentSentUiThreadCallback,
             animationController = animationController,
+            dismissShade = true,
         )
     }
 
@@ -89,11 +95,13 @@
             intentSentUiThreadCallback = intentSentUiThreadCallback,
             animationController = animationController,
             showOverLockscreen = true,
+            dismissShade = true,
         )
     }
 
     override fun startPendingIntentMaybeDismissingKeyguard(
         intent: PendingIntent,
+        dismissShade: Boolean,
         intentSentUiThreadCallback: Runnable?,
         animationController: ActivityTransitionAnimator.Controller?,
         fillInIntent: Intent?,
@@ -104,6 +112,7 @@
             intentSentUiThreadCallback = intentSentUiThreadCallback,
             animationController = animationController,
             showOverLockscreen = true,
+            dismissShade = dismissShade,
             fillInIntent = fillInIntent,
             extraOptions = extraOptions,
         )
@@ -179,6 +188,7 @@
             showOverLockscreenWhenLocked = showOverLockscreenWhenLocked,
         )
     }
+
     override fun startActivity(
         intent: Intent,
         dismissShade: Boolean,
@@ -199,6 +209,7 @@
         postOnUiThread {
             activityStarterInternal.startPendingIntentDismissingKeyguard(
                 intent = intent,
+                dismissShade = true,
             )
         }
     }
@@ -211,6 +222,7 @@
             activityStarterInternal.startPendingIntentDismissingKeyguard(
                 intent = intent,
                 animationController = animationController,
+                dismissShade = true,
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt
index e844398..c9becb4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternal.kt
@@ -34,6 +34,7 @@
      */
     fun startPendingIntentDismissingKeyguard(
         intent: PendingIntent,
+        dismissShade: Boolean,
         intentSentUiThreadCallback: Runnable? = null,
         associatedView: View? = null,
         animationController: ActivityTransitionAnimator.Controller? = null,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
index c101755..e580f64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterInternalImpl.kt
@@ -35,6 +35,7 @@
 class ActivityStarterInternalImpl @Inject constructor() : ActivityStarterInternal {
     override fun startPendingIntentDismissingKeyguard(
         intent: PendingIntent,
+        dismissShade: Boolean,
         intentSentUiThreadCallback: Runnable?,
         associatedView: View?,
         animationController: ActivityTransitionAnimator.Controller?,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 8fb552f..05a4391 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -199,6 +199,11 @@
 
     boolean isLaunchingActivityOverLockscreen();
 
+    /**
+     * Whether an activity launch over lockscreen is causing the shade to be dismissed.
+     */
+    boolean isDismissingShadeForActivityLaunch();
+
     void onKeyguardViewManagerStatesUpdated();
 
     /**  */
@@ -322,6 +327,11 @@
     @Deprecated
     float getDisplayDensity();
 
+    /**
+     * Forwards touch events to communal hub
+     */
+    void handleCommunalHubTouch(MotionEvent event);
+
     public static class KeyboardShortcutsMessage {
         final int mDeviceId;
 
@@ -333,7 +343,8 @@
     /**
      * Sets launching activity over LS state in central surfaces.
      */
-    void setIsLaunchingActivityOverLockscreen(boolean isLaunchingActivityOverLockscreen);
+    void setIsLaunchingActivityOverLockscreen(
+            boolean isLaunchingActivityOverLockscreen, boolean dismissShade);
 
     /**
      * Gets an animation controller from a notification row.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
index 8af7ee8..a7b5484 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
@@ -39,6 +39,7 @@
     override fun updateIsKeyguard(forceStateChange: Boolean) = false
     override fun getKeyguardMessageArea(): AuthKeyguardMessageArea? = null
     override fun isLaunchingActivityOverLockscreen() = false
+    override fun isDismissingShadeForActivityLaunch() = false
     override fun onKeyguardViewManagerStatesUpdated() {}
     override fun getCommandQueuePanelsEnabled() = false
     override fun showWirelessChargingAnimation(batteryLevel: Int) {}
@@ -80,6 +81,7 @@
     override fun shouldIgnoreTouch() = false
     override fun isDeviceInteractive() = false
     override fun handleDreamTouch(event: MotionEvent?) {}
+    override fun handleCommunalHubTouch(event: MotionEvent?) {}
     override fun awakenDreams() {}
     override fun isBouncerShowing() = false
     override fun isBouncerShowingScrimmed() = false
@@ -96,7 +98,10 @@
     override fun setLaunchEmergencyActionOnFinishedWaking(launch: Boolean) {}
     override fun getQSPanelController(): QSPanelController? = null
     override fun getDisplayDensity() = 0f
-    override fun setIsLaunchingActivityOverLockscreen(isLaunchingActivityOverLockscreen: Boolean) {}
+    override fun setIsLaunchingActivityOverLockscreen(
+        isLaunchingActivityOverLockscreen: Boolean,
+        dismissShade: Boolean,
+    ) {}
     override fun getAnimatorControllerFromNotification(
         associatedView: ExpandableNotificationRow?,
     ): ActivityTransitionAnimator.Controller? = null
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 d3d2b1e..7567f36 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -172,6 +172,7 @@
 import com.android.systemui.settings.brightness.BrightnessSliderController;
 import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor;
 import com.android.systemui.shade.CameraLauncher;
+import com.android.systemui.shade.GlanceableHubContainerController;
 import com.android.systemui.shade.NotificationShadeWindowView;
 import com.android.systemui.shade.NotificationShadeWindowViewController;
 import com.android.systemui.shade.QuickSettingsController;
@@ -544,6 +545,7 @@
     // Fingerprint (as computed by getLoggingFingerprint() of the last logged state.
     private int mLastLoggedStateFingerprint;
     private boolean mIsLaunchingActivityOverLockscreen;
+    private boolean mDismissingShadeForActivityLaunch;
 
     private final LifecycleRegistry mLifecycle = new LifecycleRegistry(this);
     protected final BatteryController mBatteryController;
@@ -594,6 +596,7 @@
     private final ColorExtractor.OnColorsChangedListener mOnColorsChangedListener =
             (extractor, which) -> updateTheme();
     private final BrightnessMirrorShowingInteractor mBrightnessMirrorShowingInteractor;
+    private final GlanceableHubContainerController mGlanceableHubContainerController;
 
     /**
      * Public constructor for CentralSurfaces.
@@ -706,7 +709,8 @@
             UserTracker userTracker,
             Provider<FingerprintManager> fingerprintManager,
             ActivityStarter activityStarter,
-            BrightnessMirrorShowingInteractor brightnessMirrorShowingInteractor
+            BrightnessMirrorShowingInteractor brightnessMirrorShowingInteractor,
+            GlanceableHubContainerController glanceableHubContainerController
     ) {
         mContext = context;
         mNotificationsController = notificationsController;
@@ -801,6 +805,7 @@
         mFingerprintManager = fingerprintManager;
         mActivityStarter = activityStarter;
         mBrightnessMirrorShowingInteractor = brightnessMirrorShowingInteractor;
+        mGlanceableHubContainerController = glanceableHubContainerController;
 
         mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
         mStartingSurfaceOptional = startingSurfaceOptional;
@@ -1575,6 +1580,11 @@
         return mIsLaunchingActivityOverLockscreen;
     }
 
+    @Override
+    public boolean isDismissingShadeForActivityLaunch() {
+        return mDismissingShadeForActivityLaunch;
+    }
+
     /**
      * To be called when there's a state change in StatusBarKeyguardViewManager.
      */
@@ -2790,6 +2800,10 @@
     @Override
     @VisibleForTesting
     public void updateScrimController() {
+        if (SceneContainerFlag.isEnabled()) {
+            return;
+        }
+
         Trace.beginSection("CentralSurfaces#updateScrimController");
 
         boolean unlocking = mKeyguardStateController.isShowing() && (
@@ -2812,15 +2826,15 @@
                     // Assume scrim state for shade is already correct and do nothing
                 } else {
                     // Safeguard which prevents the scrim from being stuck in the wrong state
-                    mScrimController.transitionTo(ScrimState.KEYGUARD);
+                    mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
                 }
             } else {
                 if ((!mKeyguardStateController.isOccluded() || mShadeSurface.isPanelExpanded())
                         && (mState == StatusBarState.SHADE || mState == StatusBarState.SHADE_LOCKED
                         || mTransitionToFullShadeProgress > 0f)) {
-                    mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
+                    mScrimController.legacyTransitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
                 } else {
-                    mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
+                    mScrimController.legacyTransitionTo(ScrimState.AUTH_SCRIMMED);
                 }
             }
             // This will cancel the keyguardFadingAway animation if it is running. We need to do
@@ -2832,40 +2846,40 @@
             // FLAG_DISMISS_KEYGUARD_ACTIVITY.
             ScrimState state = mStatusBarKeyguardViewManager.primaryBouncerNeedsScrimming()
                     ? ScrimState.BOUNCER_SCRIMMED : ScrimState.BOUNCER;
-            mScrimController.transitionTo(state);
+            mScrimController.legacyTransitionTo(state);
         } else if (mBrightnessMirrorVisible) {
-            mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR);
+            mScrimController.legacyTransitionTo(ScrimState.BRIGHTNESS_MIRROR);
         } else if (mState == StatusBarState.SHADE_LOCKED) {
-            mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
+            mScrimController.legacyTransitionTo(ScrimState.SHADE_LOCKED);
         } else if (mDozeServiceHost.isPulsing()) {
-            mScrimController.transitionTo(ScrimState.PULSING,
+            mScrimController.legacyTransitionTo(ScrimState.PULSING,
                     mDozeScrimController.getScrimCallback());
         } else if (mDozeServiceHost.hasPendingScreenOffCallback()) {
-            mScrimController.transitionTo(ScrimState.OFF, new ScrimController.Callback() {
+            mScrimController.legacyTransitionTo(ScrimState.OFF, new ScrimController.Callback() {
                 @Override
                 public void onFinished() {
                     mDozeServiceHost.executePendingScreenOffCallback();
                 }
             });
         } else if (mDozing && !unlocking) {
-            mScrimController.transitionTo(ScrimState.AOD);
+            mScrimController.legacyTransitionTo(ScrimState.AOD);
             // This will cancel the keyguardFadingAway animation if it is running. We need to do
             // this as otherwise it can remain pending and leave keyguard in a weird state.
             mUnlockScrimCallback.onCancelled();
         } else if (mIsIdleOnCommunal) {
             if (dreaming) {
-                mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB_OVER_DREAM);
+                mScrimController.legacyTransitionTo(ScrimState.GLANCEABLE_HUB_OVER_DREAM);
             } else {
-                mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB);
+                mScrimController.legacyTransitionTo(ScrimState.GLANCEABLE_HUB);
             }
         } else if (mKeyguardStateController.isShowing()
                 && !mKeyguardStateController.isOccluded()
                 && !unlocking) {
-            mScrimController.transitionTo(ScrimState.KEYGUARD);
+            mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
         } else if (dreaming) {
-            mScrimController.transitionTo(ScrimState.DREAMING);
+            mScrimController.legacyTransitionTo(ScrimState.DREAMING);
         } else {
-            mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
+            mScrimController.legacyTransitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
         }
         updateLightRevealScrimVisibility();
 
@@ -2945,6 +2959,11 @@
     }
 
     @Override
+    public void handleCommunalHubTouch(MotionEvent event) {
+        mGlanceableHubContainerController.onTouchEvent(event);
+    }
+
+    @Override
     public void awakenDreams() {
         mUiBgExecutor.execute(() -> {
             try {
@@ -3306,8 +3325,10 @@
     }
 
     @Override
-    public void setIsLaunchingActivityOverLockscreen(boolean isLaunchingActivityOverLockscreen) {
+    public void setIsLaunchingActivityOverLockscreen(
+            boolean isLaunchingActivityOverLockscreen, boolean dismissShade) {
         mIsLaunchingActivityOverLockscreen = isLaunchingActivityOverLockscreen;
+        mDismissingShadeForActivityLaunch = dismissShade;
         mKeyguardViewMediator.launchingActivityOverLockscreen(mIsLaunchingActivityOverLockscreen);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 77f3706..330383f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -29,6 +29,8 @@
 import android.view.MotionEvent;
 import android.view.View;
 
+import androidx.annotation.Nullable;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.assist.AssistManager;
@@ -74,6 +76,11 @@
     private final PowerManager mPowerManager;
     private boolean mAnimateWakeup;
     private boolean mIgnoreTouchWhilePulsing;
+    private final HasPendingScreenOffCallbackChangeListener
+            mDefaultHasPendingScreenOffCallbackChangeListener =
+                    hasPendingScreenOffCallback -> { /* no op */ };
+    private HasPendingScreenOffCallbackChangeListener mHasPendingScreenOffCallbackChangeListener =
+            mDefaultHasPendingScreenOffCallbackChangeListener;
     private Runnable mPendingScreenOffCallback;
     @VisibleForTesting
     boolean mWakeLockScreenPerformsAuth = SystemProperties.getBoolean(
@@ -434,12 +441,14 @@
             Log.w(TAG, "Overlapping onDisplayOffCallback. Ignoring previous one.");
         }
         mPendingScreenOffCallback = onDisplayOffCallback;
+        mHasPendingScreenOffCallbackChangeListener.onHasPendingScreenOffCallbackChanged(true);
         mCentralSurfaces.updateScrimController();
     }
 
     @Override
     public void cancelGentleSleep() {
         mPendingScreenOffCallback = null;
+        mHasPendingScreenOffCallbackChangeListener.onHasPendingScreenOffCallbackChanged(false);
         if (mScrimController.getState() == ScrimState.OFF) {
             mCentralSurfaces.updateScrimController();
         }
@@ -448,11 +457,27 @@
     /**
      * When the dozing host is waiting for scrims to fade out to change the display state.
      */
-    boolean hasPendingScreenOffCallback() {
+    public boolean hasPendingScreenOffCallback() {
         return mPendingScreenOffCallback != null;
     }
 
     /**
+     * Sets a listener to be notified whenever the result of {@link #hasPendingScreenOffCallback()}
+     * changes.
+     *
+     * <p>Setting the listener automatically notifies the listener inline.
+     */
+    public void setHasPendingScreenOffCallbackChangeListener(
+            @Nullable HasPendingScreenOffCallbackChangeListener listener) {
+        mHasPendingScreenOffCallbackChangeListener = listener != null
+                ? listener
+                : mDefaultHasPendingScreenOffCallbackChangeListener;
+
+        mHasPendingScreenOffCallbackChangeListener.onHasPendingScreenOffCallbackChanged(
+                mPendingScreenOffCallback != null);
+    }
+
+    /**
      * Executes an nullifies the pending display state callback.
      *
      * @see #hasPendingScreenOffCallback()
@@ -464,6 +489,7 @@
         }
         mPendingScreenOffCallback.run();
         mPendingScreenOffCallback = null;
+        mHasPendingScreenOffCallbackChangeListener.onHasPendingScreenOffCallbackChanged(false);
     }
 
     boolean shouldAnimateWakeup() {
@@ -524,4 +550,14 @@
             }
         }
     };
+
+    /**
+     * Defines interface for classes that can be notified about changes to having or not having a
+     * pending screen-off callback.
+     */
+    public interface HasPendingScreenOffCallbackChangeListener {
+
+        /** Notifies that there now is or isn't a pending screen-off callback. */
+        void onHasPendingScreenOffCallbackChanged(boolean hasPendingScreenOffCallback);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index ffc859e..4bf122d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -86,6 +86,8 @@
     private final List<OnHeadsUpPhoneListenerChange> mHeadsUpPhoneListeners = new ArrayList<>();
     private final VisualStabilityProvider mVisualStabilityProvider;
 
+    private final AvalancheController mAvalancheController;
+
     // TODO(b/328393698) move the topHeadsUpRow logic to an interactor
     private final MutableStateFlow<HeadsUpRowRepository> mTopHeadsUpRow =
             StateFlowKt.MutableStateFlow(null);
@@ -155,6 +157,7 @@
         mBypassController = bypassController;
         mGroupMembershipManager = groupMembershipManager;
         mVisualStabilityProvider = visualStabilityProvider;
+        mAvalancheController = avalancheController;
 
         updateResources();
         configurationController.addCallback(new ConfigurationController.ConfigurationListener() {
@@ -653,9 +656,10 @@
             boolean wasKeyguard = mStatusBarState == StatusBarState.KEYGUARD;
             boolean isKeyguard = newState == StatusBarState.KEYGUARD;
             mStatusBarState = newState;
+
             if (wasKeyguard && !isKeyguard && mBypassController.getBypassEnabled()) {
                 ArrayList<String> keysToRemove = new ArrayList<>();
-                for (HeadsUpEntry entry : mHeadsUpEntryMap.values()) {
+                for (HeadsUpEntry entry : getHeadsUpEntryList()) {
                     if (entry.mEntry != null && entry.mEntry.isBubble() && !entry.isSticky()) {
                         keysToRemove.add(entry.mEntry.getKey());
                     }
@@ -671,7 +675,7 @@
             if (!isDozing) {
                 // Let's make sure all huns we got while dozing time out within the normal timeout
                 // duration. Otherwise they could get stuck for a very long time
-                for (HeadsUpEntry entry : mHeadsUpEntryMap.values()) {
+                for (HeadsUpEntry entry : getHeadsUpEntryList()) {
                     entry.updateEntry(true /* updatePostTime */, "onDozingChanged(false)");
                 }
             }
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 6d4301f..e44edcb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -398,7 +398,7 @@
         mSystemIconsContainer.setOnHoverListener(hoverListener);
         mView.setOnApplyWindowInsetsListener(
                 (view, windowInsets) -> mView.updateWindowInsets(windowInsets, mInsetsProvider));
-        mSecureSettings.registerContentObserverForUser(
+        mSecureSettings.registerContentObserverForUserSync(
                 Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON,
                 false,
                 mVolumeSettingObserver,
@@ -416,7 +416,7 @@
         mStatusBarStateController.removeCallback(mStatusBarStateListener);
         mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
         mDisableStateTracker.stopTracking(mCommandQueue);
-        mSecureSettings.unregisterContentObserver(mVolumeSettingObserver);
+        mSecureSettings.unregisterContentObserverSync(mVolumeSettingObserver);
         if (mTintedIconManager != null) {
             mStatusBarIconController.removeIconGroup(mTintedIconManager);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
index bcc7db1..fc29eab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
@@ -33,10 +33,13 @@
 import android.view.WindowManager
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.ActivityIntentHelper
+import com.android.systemui.Flags.communalHub
 import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.animation.DelegateTransitionAnimatorController
 import com.android.systemui.assist.AssistManager
 import com.android.systemui.camera.CameraIntents
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.DisplayId
 import com.android.systemui.dagger.qualifiers.Main
@@ -89,6 +92,7 @@
     private val userTracker: UserTracker,
     private val activityIntentHelper: ActivityIntentHelper,
     @Main private val mainExecutor: DelayableExecutor,
+    private val communalSceneInteractor: CommunalSceneInteractor,
 ) : ActivityStarterInternal {
     private val centralSurfaces: CentralSurfaces?
         get() = centralSurfacesOptLazy.get().getOrNull()
@@ -219,6 +223,7 @@
 
     override fun startPendingIntentDismissingKeyguard(
         intent: PendingIntent,
+        dismissShade: Boolean,
         intentSentUiThreadCallback: Runnable?,
         associatedView: View?,
         animationController: ActivityTransitionAnimator.Controller?,
@@ -257,12 +262,12 @@
         val statusBarController =
             wrapAnimationControllerForShadeOrStatusBar(
                 animationController = animationController,
-                dismissShade = true,
+                dismissShade = dismissShade,
                 isLaunchForActivity = intent.isActivity,
             )
         val controller =
             if (actuallyShowOverLockscreen) {
-                wrapAnimationControllerForLockscreen(statusBarController)
+                wrapAnimationControllerForLockscreen(dismissShade, statusBarController)
             } else {
                 statusBarController
             }
@@ -270,7 +275,7 @@
         // If we animate, don't collapse the shade and defer the keyguard dismiss (in case we
         // run the animation on the keyguard). The animation will take care of (instantly)
         // collapsing the shade and hiding the keyguard once it is done.
-        val collapse = !animate
+        val collapse = dismissShade && !animate
         val runnable = Runnable {
             try {
                 activityTransitionAnimator.startPendingIntentWithAnimation(
@@ -377,7 +382,7 @@
                     dismissShade = dismissShade,
                     isLaunchForActivity = true,
                 )
-            controller = wrapAnimationControllerForLockscreen(delegate)
+            controller = wrapAnimationControllerForLockscreen(dismissShade, delegate)
         } else if (dismissShade) {
             // The animation will take care of dismissing the shade at the end of the animation.
             // If we don't animate, collapse it directly.
@@ -408,6 +413,7 @@
         afterKeyguardGone: Boolean,
         customMessage: String?,
     ) {
+        Log.i(TAG, "Invoking dismissKeyguardThenExecute, afterKeyguardGone: $afterKeyguardGone")
         if (
             !action.willRunAnimationOnKeyguard() &&
                 wakefulnessLifecycle.wakefulness == WakefulnessLifecycle.WAKEFULNESS_ASLEEP &&
@@ -462,6 +468,9 @@
                     if (dismissShade) {
                         shadeControllerLazy.get().collapseShadeForActivityStart()
                     }
+                    if (communalHub()) {
+                        communalSceneInteractor.snapToScene(CommunalScenes.Blank)
+                    }
                     return deferred
                 }
 
@@ -532,6 +541,7 @@
      * lockscreen, the correct flags are set for it to be occluded.
      */
     private fun wrapAnimationControllerForLockscreen(
+        dismissShade: Boolean,
         animationController: ActivityTransitionAnimator.Controller?
     ): ActivityTransitionAnimator.Controller? {
         return animationController?.let {
@@ -539,7 +549,7 @@
                 override fun onIntentStarted(willAnimate: Boolean) {
                     delegate.onIntentStarted(willAnimate)
                     if (willAnimate) {
-                        centralSurfaces?.setIsLaunchingActivityOverLockscreen(true)
+                        centralSurfaces?.setIsLaunchingActivityOverLockscreen(true, dismissShade)
                     }
                 }
 
@@ -570,7 +580,10 @@
                     // mIsLaunchingActivityOverLockscreen being true means that we will
                     // collapse the shade (or at least run the post collapse runnables)
                     // later on.
-                    centralSurfaces?.setIsLaunchingActivityOverLockscreen(false)
+                    centralSurfaces?.setIsLaunchingActivityOverLockscreen(false, false)
+                    if (communalHub()) {
+                        communalSceneInteractor.snapToScene(CommunalScenes.Blank)
+                    }
                     delegate.onTransitionAnimationEnd(isExpandingFullyAbove)
                 }
 
@@ -586,7 +599,7 @@
                     // mIsLaunchingActivityOverLockscreen being true means that we will
                     // collapse the shade (or at least run the // post collapse
                     // runnables) later on.
-                    centralSurfaces?.setIsLaunchingActivityOverLockscreen(false)
+                    centralSurfaces?.setIsLaunchingActivityOverLockscreen(false, false)
                     delegate.onTransitionAnimationCancelled(newKeyguardOccludedState)
                 }
             }
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 fe001b3..9cece76 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -72,6 +72,7 @@
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
 import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.scene.shared.model.Scenes;
 import com.android.systemui.scrim.ScrimView;
 import com.android.systemui.shade.ShadeViewController;
@@ -319,8 +320,11 @@
         mScrimBehind.setViewAlpha(mBehindAlpha);
     };
 
+    @VisibleForTesting
     Consumer<TransitionStep> mBouncerToGoneTransition;
 
+    private boolean mViewsAttached;
+
     @Inject
     public ScrimController(
             LightBarController lightBarController,
@@ -432,6 +436,16 @@
             state.prepare(state);
         }
 
+        hydrateStateInternally(behindScrim);
+
+        mViewsAttached = true;
+    }
+
+    private void hydrateStateInternally(ScrimView behindScrim) {
+        if (SceneContainerFlag.isEnabled()) {
+            return;
+        }
+
         // Directly control transition to UNLOCKED scrim state from PRIMARY_BOUNCER, and make sure
         // to report back that keyguard has faded away. This fixes cases where the scrim state was
         // rapidly switching on unlock, due to shifts in state in CentralSurfacesImpl
@@ -443,7 +457,7 @@
 
                     if (state == TransitionState.STARTED) {
                         setExpansionAffectsAlpha(false);
-                        transitionTo(ScrimState.UNLOCKED);
+                        legacyTransitionTo(ScrimState.UNLOCKED);
                     }
 
                     if (state == TransitionState.FINISHED || state == TransitionState.CANCELED) {
@@ -508,10 +522,36 @@
     }
 
     public void transitionTo(ScrimState state) {
-        transitionTo(state, null);
+        if (SceneContainerFlag.isUnexpectedlyInLegacyMode() || !mViewsAttached) {
+            return;
+        }
+
+        internalTransitionTo(state, null);
     }
 
-    public void transitionTo(ScrimState state, Callback callback) {
+    /**
+     * Transitions to the given {@link ScrimState}.
+     *
+     * @deprecated Legacy codepath only. Do not call directly.
+     */
+    @Deprecated
+    public void legacyTransitionTo(ScrimState state) {
+        SceneContainerFlag.assertInLegacyMode();
+        internalTransitionTo(state, null);
+    }
+
+    /**
+     * Transitions to the given {@link ScrimState}.
+     *
+     * @deprecated Legacy codepath only. Do not call directly.
+     */
+    @Deprecated
+    public void legacyTransitionTo(ScrimState state, Callback callback) {
+        SceneContainerFlag.assertInLegacyMode();
+        internalTransitionTo(state, callback);
+    }
+
+    private void internalTransitionTo(ScrimState state, Callback callback) {
         if (mIsBouncerToGoneTransitionRunning) {
             Log.i(TAG, "Skipping transition to: " + state
                     + " while mIsBouncerToGoneTransitionRunning");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 5bbc3bd..db4f0af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -112,6 +112,7 @@
 import kotlinx.coroutines.Job;
 
 import java.io.PrintWriter;
+import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Objects;
@@ -899,6 +900,11 @@
             } finally {
                 Trace.endSection();
             }
+        } else {
+            Log.w(TAG, "Ignoring request to dismiss, dumping state: ");
+            StringWriter sw = new StringWriter();
+            mKeyguardStateController.dump(new PrintWriter(sw), null);
+            Log.w(TAG, sw.toString());
         }
         updateStates();
     }
@@ -1071,12 +1077,17 @@
             SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED,
                     SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__OCCLUDED);
             if (mCentralSurfaces.isLaunchingActivityOverLockscreen()) {
-                // When isLaunchingActivityOverLockscreen() is true, we know for sure that the post
-                // collapse runnables will be run.
-                mShadeController.get().addPostCollapseAction(() -> {
+                final Runnable postCollapseAction = () -> {
                     mNotificationShadeWindowController.setKeyguardOccluded(isOccluded);
                     reset(true /* hideBouncerWhenShowing */);
-                });
+                };
+                if (mCentralSurfaces.isDismissingShadeForActivityLaunch()) {
+                    // When isDismissingShadeForActivityLaunch() is true, we know for sure that the
+                    // post collapse runnables will be run.
+                    mShadeController.get().addPostCollapseAction(postCollapseAction);
+                } else {
+                    postCollapseAction.run();
+                }
                 return;
             }
         } else if (isShowing && isUnOccluding) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 479aef1..c53558e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -23,9 +23,9 @@
 import com.android.systemui.keyguard.KeyguardViewMediator
 import com.android.systemui.keyguard.MigrateClocksToBlueprint
 import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractor
 import com.android.systemui.shade.ShadeViewController
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
+import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractor
 import com.android.systemui.statusbar.CircleReveal
 import com.android.systemui.statusbar.LightRevealScrim
 import com.android.systemui.statusbar.NotificationShadeWindowController
@@ -162,7 +162,7 @@
         this.centralSurfaces = centralSurfaces
 
         updateAnimatorDurationScale()
-        globalSettings.registerContentObserver(
+        globalSettings.registerContentObserverSync(
             Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
             /* notify for descendants */ false,
             animatorDurationScaleObserver
@@ -376,8 +376,9 @@
         // We currently draw both the light reveal scrim, and the AOD UI, in the shade. If it's
         // already expanded and showing notifications/QS, the animation looks really messy. For now,
         // disable it if the notification panel is expanded.
-        if ((!this::centralSurfaces.isInitialized ||
-                    panelExpansionInteractorLazy.get().isPanelExpanded) &&
+        if (
+            (!this::centralSurfaces.isInitialized ||
+                panelExpansionInteractorLazy.get().isPanelExpanded) &&
                 // Status bar might be expanded because we have started
                 // playing the animation already
                 !isAnimationPlaying()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 97f9e06..3d8090d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -39,6 +39,7 @@
 import com.android.app.animation.InterpolatorsAndroidX;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.Dumpable;
+import com.android.systemui.Flags;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.demomode.DemoModeController;
@@ -51,7 +52,6 @@
 import com.android.systemui.statusbar.OperatorNameView;
 import com.android.systemui.statusbar.OperatorNameViewController;
 import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModel;
 import com.android.systemui.statusbar.disableflags.DisableFlagsLogger.DisableState;
 import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
@@ -80,6 +80,10 @@
 import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener;
 import com.android.systemui.util.settings.SecureSettings;
 
+import kotlin.Unit;
+
+import kotlinx.coroutines.DisposableHandle;
+
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -90,10 +94,6 @@
 
 import javax.inject.Inject;
 
-import kotlin.Unit;
-
-import kotlinx.coroutines.DisposableHandle;
-
 /**
  * Contains the collapsed status bar and handles hiding/showing based on disable flags
  * and keyguard state. Also manages lifecycle to make sure the views it contains are being
@@ -135,8 +135,6 @@
     private final CollapsedStatusBarFragmentLogger mCollapsedStatusBarFragmentLogger;
     private final OperatorNameViewController.Factory mOperatorNameViewControllerFactory;
     private final OngoingCallController mOngoingCallController;
-    // TODO(b/332662551): Use this view model to show the ongoing activity chips.
-    private final OngoingActivityChipsViewModel mOngoingActivityChipsViewModel;
     private final SystemStatusAnimationScheduler mAnimationScheduler;
     private final StatusBarLocationPublisher mLocationPublisher;
     private final NotificationIconAreaController mNotificationIconAreaController;
@@ -207,6 +205,11 @@
     private boolean mTransitionFromLockscreenToDreamStarted = false;
 
     /**
+     * True if there's an active ongoing activity that should be showing a chip and false otherwise.
+     */
+    private boolean mHasOngoingActivity;
+
+    /**
      * Listener that updates {@link #mWaitingForWindowStateChangeAfterCameraLaunch} when it receives
      * a new status bar window state.
      */
@@ -216,11 +219,12 @@
     };
     private DisposableHandle mNicBindingDisposable;
 
+    private boolean mAnimationsEnabled = true;
+
     @Inject
     public CollapsedStatusBarFragment(
             StatusBarFragmentComponent.Factory statusBarFragmentComponentFactory,
             OngoingCallController ongoingCallController,
-            OngoingActivityChipsViewModel ongoingActivityChipsViewModel,
             SystemStatusAnimationScheduler animationScheduler,
             StatusBarLocationPublisher locationPublisher,
             NotificationIconAreaController notificationIconAreaController,
@@ -246,7 +250,6 @@
             DemoModeController demoModeController) {
         mStatusBarFragmentComponentFactory = statusBarFragmentComponentFactory;
         mOngoingCallController = ongoingCallController;
-        mOngoingActivityChipsViewModel = ongoingActivityChipsViewModel;
         mAnimationScheduler = animationScheduler;
         mLocationPublisher = locationPublisher;
         mNotificationIconAreaController = notificationIconAreaController;
@@ -401,6 +404,17 @@
         return mBlockedIcons;
     }
 
+
+    @VisibleForTesting
+    void enableAnimationsForTesting() {
+        mAnimationsEnabled = true;
+    }
+
+    @VisibleForTesting
+    void disableAnimationsForTesting() {
+        mAnimationsEnabled = false;
+    }
+
     @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
@@ -417,7 +431,7 @@
         initOngoingCallChip();
         mAnimationScheduler.addCallback(this);
 
-        mSecureSettings.registerContentObserverForUser(
+        mSecureSettings.registerContentObserverForUserSync(
                 Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON,
                 false,
                 mVolumeSettingObserver,
@@ -431,7 +445,7 @@
         mStatusBarStateController.removeCallback(this);
         mOngoingCallController.removeCallback(mOngoingCallListener);
         mAnimationScheduler.removeCallback(this);
-        mSecureSettings.unregisterContentObserver(mVolumeSettingObserver);
+        mSecureSettings.unregisterContentObserverSync(mVolumeSettingObserver);
     }
 
     @Override
@@ -476,7 +490,7 @@
             notificationIconArea.addView(mNotificationIconAreaInner);
         }
 
-        updateNotificationIconAreaAndCallChip(/* animate= */ false);
+        updateNotificationIconAreaAndOngoingActivityChip(/* animate= */ false);
         Trace.endSection();
     }
 
@@ -493,15 +507,21 @@
 
     private StatusBarVisibilityChangeListener mStatusBarVisibilityChangeListener =
             new StatusBarVisibilityChangeListener() {
-        @Override
-        public void onStatusBarVisibilityMaybeChanged() {
-            updateStatusBarVisibilities(/* animate= */ true);
-        }
+                @Override
+                public void onStatusBarVisibilityMaybeChanged() {
+                    updateStatusBarVisibilities(/* animate= */ true);
+                }
 
-        @Override
-        public void onTransitionFromLockscreenToDreamStarted() {
-            mTransitionFromLockscreenToDreamStarted = true;
-        }
+                @Override
+                public void onTransitionFromLockscreenToDreamStarted() {
+                    mTransitionFromLockscreenToDreamStarted = true;
+                }
+
+                @Override
+                public void onOngoingActivityStatusChanged(boolean hasOngoingActivity) {
+                    mHasOngoingActivity = hasOngoingActivity;
+                    updateStatusBarVisibilities(/* animate= */ true);
+                }
     };
 
     @Override
@@ -532,11 +552,14 @@
             }
         }
 
-        // The ongoing call chip and notification icon visibilities are intertwined, so update both
-        // if either change.
-        if (newModel.getShowNotificationIcons() != previousModel.getShowNotificationIcons()
-                || newModel.getShowOngoingCallChip() != previousModel.getShowOngoingCallChip()) {
-            updateNotificationIconAreaAndCallChip(animate);
+        // The ongoing activity chip and notification icon visibilities are intertwined, so update
+        // both if either change.
+        boolean notifsChanged =
+                newModel.getShowNotificationIcons() != previousModel.getShowNotificationIcons();
+        boolean ongoingActivityChanged =
+                newModel.getShowOngoingActivityChip() != previousModel.getShowOngoingActivityChip();
+        if (notifsChanged || ongoingActivityChanged) {
+            updateNotificationIconAreaAndOngoingActivityChip(animate);
         }
 
         // The clock may have already been hidden, but we might want to shift its
@@ -566,45 +589,58 @@
             return new StatusBarVisibilityModel(
                     /* showClock= */ false,
                     /* showNotificationIcons= */ false,
-                    /* showOngoingCallChip= */ false,
+                    /* showOngoingActivityChip= */ false,
                     /* showSystemInfo= */ false);
         }
 
         boolean showClock = externalModel.getShowClock() && !headsUpVisible;
-        boolean showOngoingCallChip = mOngoingCallController.hasOngoingCall() && !headsUpVisible;
+
+        boolean showOngoingActivityChip;
+        if (Flags.statusBarScreenSharingChips()) {
+            // If this flag is on, the ongoing activity status comes from
+            // CollapsedStatusBarViewBinder, which updates the mHasOngoingActivity variable.
+            showOngoingActivityChip = mHasOngoingActivity;
+        } else {
+            // If this flag is off, the only ongoing activity is the ongoing call, and we pull it
+            // from the controller directly.
+            showOngoingActivityChip = mOngoingCallController.hasOngoingCall();
+        }
+
         return new StatusBarVisibilityModel(
                 showClock,
                 externalModel.getShowNotificationIcons(),
-                showOngoingCallChip,
+                showOngoingActivityChip && !headsUpVisible,
                 externalModel.getShowSystemInfo());
     }
 
     /**
-     * Updates the visibility of the notification icon area and ongoing call chip based on disabled1
-     * state.
+     * Updates the visibility of the notification icon area and ongoing activity chip based on
+     * mLastModifiedVisibility.
      */
-    private void updateNotificationIconAreaAndCallChip(boolean animate) {
+    private void updateNotificationIconAreaAndOngoingActivityChip(boolean animate) {
         StatusBarVisibilityModel visibilityModel = mLastModifiedVisibility;
         boolean disableNotifications = !visibilityModel.getShowNotificationIcons();
-        boolean hasOngoingCall = visibilityModel.getShowOngoingCallChip();
+        boolean hasOngoingActivity = visibilityModel.getShowOngoingActivityChip();
 
-        // Hide notifications if the disable flag is set or we have an ongoing call.
-        if (disableNotifications || hasOngoingCall) {
-            hideNotificationIconArea(animate && !hasOngoingCall);
+        // Hide notifications if the disable flag is set or we have an ongoing activity.
+        if (disableNotifications || hasOngoingActivity) {
+            hideNotificationIconArea(animate && !hasOngoingActivity);
         } else {
             showNotificationIconArea(animate);
         }
 
-        // Show the ongoing call chip only if there is an ongoing call *and* notification icons
-        // are allowed. (The ongoing call chip occupies the same area as the notification icons,
-        // so if the icons are disabled then the call chip should be, too.)
-        boolean showOngoingCallChip = hasOngoingCall && !disableNotifications;
-        if (showOngoingCallChip) {
+        // Show the ongoing activity chip only if there is an ongoing activity *and* notification
+        // icons are allowed. (The ongoing activity chip occupies the same area as the notification,
+        // icons so if the icons are disabled then the activity chip should be, too.)
+        boolean showOngoingActivityChip = hasOngoingActivity && !disableNotifications;
+        if (showOngoingActivityChip) {
             showOngoingActivityChip(animate);
         } else {
             hideOngoingActivityChip(animate);
         }
-        mOngoingCallController.notifyChipVisibilityChanged(showOngoingCallChip);
+        if (!Flags.statusBarScreenSharingChips()) {
+            mOngoingCallController.notifyChipVisibilityChanged(showOngoingActivityChip);
+        }
     }
 
     private boolean shouldHideStatusBar() {
@@ -702,8 +738,9 @@
     /**
      * Displays the ongoing activity chip.
      *
-     * For now, this chip will only ever contain the ongoing call information, but after b/332662551
-     * feature is implemented, it will support different kinds of ongoing activities.
+     * If Flags.statusBarScreenSharingChips is disabled, this chip will only ever contain the
+     * ongoing call information, If that flag is enabled, it will support different kinds of ongoing
+     * activities. See b/332662551.
      */
     private void showOngoingActivityChip(boolean animate) {
         animateShow(mOngoingActivityChip, animate);
@@ -746,7 +783,7 @@
      */
     private void animateHiddenState(final View v, int state, boolean animate) {
         v.animate().cancel();
-        if (!animate) {
+        if (!animate || !mAnimationsEnabled) {
             v.setAlpha(0f);
             v.setVisibility(state);
             return;
@@ -773,7 +810,7 @@
     private void animateShow(View v, boolean animate) {
         v.animate().cancel();
         v.setVisibility(View.VISIBLE);
-        if (!animate) {
+        if (!animate || !mAnimationsEnabled) {
             v.setAlpha(1f);
             return;
         }
@@ -872,6 +909,8 @@
     @Override
     public void dump(PrintWriter printWriter, String[] args) {
         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, /* singleIndent= */"  ");
+        pw.println("mHasOngoingActivity=" + mHasOngoingActivity);
+        pw.println("mAnimationsEnabled=" + mAnimationsEnabled);
         StatusBarFragmentComponent component = mStatusBarFragmentComponent;
         if (component == null) {
             pw.println("StatusBarFragmentComponent is null");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt
index 7cdb9c0..0a19023 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt
@@ -59,13 +59,13 @@
             {
                 bool1 = model.showClock
                 bool2 = model.showNotificationIcons
-                bool3 = model.showOngoingCallChip
+                bool3 = model.showOngoingActivityChip
                 bool4 = model.showSystemInfo
             },
             { "New visibilities calculated internally. " +
                     "showClock=$bool1 " +
                     "showNotificationIcons=$bool2 " +
-                    "showOngoingCallChip=$bool3 " +
+                    "showOngoingActivityChip=$bool3 " +
                     "showSystemInfo=$bool4"
             }
         )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt
index cf54cb7..fe24fae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModel.kt
@@ -26,7 +26,7 @@
 data class StatusBarVisibilityModel(
     val showClock: Boolean,
     val showNotificationIcons: Boolean,
-    val showOngoingCallChip: Boolean,
+    val showOngoingActivityChip: Boolean,
     val showSystemInfo: Boolean,
 ) {
     companion object {
@@ -48,7 +48,7 @@
                 showNotificationIcons = (disabled1 and DISABLE_NOTIFICATION_ICONS) == 0,
                 // TODO(b/279899176): [CollapsedStatusBarFragment] always overwrites this with the
                 //  value of [OngoingCallController]. Do we need to process the flag here?
-                showOngoingCallChip = (disabled1 and DISABLE_ONGOING_CALL_CHIP) == 0,
+                showOngoingActivityChip = (disabled1 and DISABLE_ONGOING_CALL_CHIP) == 0,
                 showSystemInfo =
                     (disabled1 and DISABLE_SYSTEM_INFO) == 0 &&
                         (disabled2 and DISABLE2_SYSTEM_ICONS) == 0
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/OemSatelliteInputLog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/DeviceBasedSatelliteInputLog.kt
similarity index 74%
copy from packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/OemSatelliteInputLog.kt
copy to packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/DeviceBasedSatelliteInputLog.kt
index 252945f..73c015d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/OemSatelliteInputLog.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/DeviceBasedSatelliteInputLog.kt
@@ -16,11 +16,14 @@
 
 package com.android.systemui.statusbar.pipeline.dagger
 
-import com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelliteRepository
 import javax.inject.Qualifier
 
-/** Detailed [DeviceBasedSatelliteRepository] logs */
+/**
+ * Logs for device-based satellite events that are **not** that frequent/chatty.
+ *
+ * For chatty logs, use [VerboseDeviceBasedSatelliteInputLog] instead.
+ */
 @Qualifier
 @MustBeDocumented
-@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
-annotation class OemSatelliteInputLog
+@Retention(AnnotationRetention.RUNTIME)
+annotation class DeviceBasedSatelliteInputLog
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 88ca9e5..a81bfa4 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
@@ -227,9 +227,16 @@
 
         @Provides
         @SysUISingleton
-        @OemSatelliteInputLog
-        fun provideOemSatelliteInputLog(factory: LogBufferFactory): LogBuffer {
-            return factory.create("DeviceBasedSatelliteInputLog", 150)
+        @DeviceBasedSatelliteInputLog
+        fun provideDeviceBasedSatelliteInputLog(factory: LogBufferFactory): LogBuffer {
+            return factory.create("DeviceBasedSatelliteInputLog", 200)
+        }
+
+        @Provides
+        @SysUISingleton
+        @VerboseDeviceBasedSatelliteInputLog
+        fun provideVerboseDeviceBasedSatelliteInputLog(factory: LogBufferFactory): LogBuffer {
+            return factory.create("VerboseDeviceBasedSatelliteInputLog", 200)
         }
 
         const val FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/OemSatelliteInputLog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/VerboseDeviceBasedSatelliteInputLog.kt
similarity index 75%
rename from packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/OemSatelliteInputLog.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/VerboseDeviceBasedSatelliteInputLog.kt
index 252945f..af68055 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/OemSatelliteInputLog.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/VerboseDeviceBasedSatelliteInputLog.kt
@@ -16,11 +16,14 @@
 
 package com.android.systemui.statusbar.pipeline.dagger
 
-import com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelliteRepository
 import javax.inject.Qualifier
 
-/** Detailed [DeviceBasedSatelliteRepository] logs */
+/**
+ * Logs for device-based satellite events that are frequent/chatty.
+ *
+ * For non-chatty logs, use [DeviceBasedSatelliteInputLog] instead.
+ */
 @Qualifier
 @MustBeDocumented
-@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
-annotation class OemSatelliteInputLog
+@Retention(AnnotationRetention.RUNTIME)
+annotation class VerboseDeviceBasedSatelliteInputLog
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index c32f0e8..261258a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -40,6 +40,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.log.table.TableLogBuffer
 import com.android.systemui.log.table.logDiffsForTable
@@ -99,6 +100,7 @@
     private val context: Context,
     @Background private val bgDispatcher: CoroutineDispatcher,
     @Application private val scope: CoroutineScope,
+    @Main private val mainDispatcher: CoroutineDispatcher,
     airplaneModeRepository: AirplaneModeRepository,
     // Some "wifi networks" should be rendered as a mobile connection, which is why the wifi
     // repository is an input to the mobile repository.
@@ -315,6 +317,7 @@
                 trySend(false)
                 awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
             }
+            .flowOn(mainDispatcher)
             .logDiffsForTable(
                 tableLogger,
                 LOGGING_PREFIX,
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 9d9cc2a..1449e53 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
@@ -23,6 +23,7 @@
 import android.telephony.satellite.SatelliteManager
 import android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS
 import android.telephony.satellite.SatelliteModemStateCallback
+import android.telephony.satellite.SatelliteSupportedStateCallback
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
 import com.android.systemui.dagger.SysUISingleton
@@ -32,8 +33,10 @@
 import com.android.systemui.log.core.LogLevel
 import com.android.systemui.log.core.MessageInitializer
 import com.android.systemui.log.core.MessagePrinter
-import com.android.systemui.statusbar.pipeline.dagger.OemSatelliteInputLog
+import com.android.systemui.statusbar.pipeline.dagger.DeviceBasedSatelliteInputLog
+import com.android.systemui.statusbar.pipeline.dagger.VerboseDeviceBasedSatelliteInputLog
 import com.android.systemui.statusbar.pipeline.satellite.data.RealDeviceBasedSatelliteRepository
+import com.android.systemui.statusbar.pipeline.satellite.data.prod.DeviceBasedSatelliteRepositoryImpl.Companion.POLLING_INTERVAL_MS
 import com.android.systemui.statusbar.pipeline.satellite.data.prod.SatelliteSupport.Companion.whenSupported
 import com.android.systemui.statusbar.pipeline.satellite.data.prod.SatelliteSupport.NotSupported
 import com.android.systemui.statusbar.pipeline.satellite.data.prod.SatelliteSupport.Supported
@@ -147,7 +150,8 @@
     telephonyManager: TelephonyManager,
     @Background private val bgDispatcher: CoroutineDispatcher,
     @Application private val scope: CoroutineScope,
-    @OemSatelliteInputLog private val logBuffer: LogBuffer,
+    @DeviceBasedSatelliteInputLog private val logBuffer: LogBuffer,
+    @VerboseDeviceBasedSatelliteInputLog private val verboseLogBuffer: LogBuffer,
     private val systemClock: SystemClock,
 ) : RealDeviceBasedSatelliteRepository {
 
@@ -160,60 +164,6 @@
     @get:VisibleForTesting
     val satelliteSupport: MutableStateFlow<SatelliteSupport> = MutableStateFlow(Unknown)
 
-    init {
-        satelliteManager = satelliteManagerOpt.getOrNull()
-
-        isSatelliteAllowedForCurrentLocation = MutableStateFlow(false)
-
-        if (satelliteManager != null) {
-            // First, check that satellite is supported on this device
-            scope.launch {
-                val waitTime = ensureMinUptime(systemClock, MIN_UPTIME)
-                if (waitTime > 0) {
-                    logBuffer.i({ long1 = waitTime }) {
-                        "Waiting $long1 ms before checking for satellite support"
-                    }
-                    delay(waitTime)
-                }
-
-                satelliteSupport.value = satelliteManager.checkSatelliteSupported()
-
-                logBuffer.i(
-                    { str1 = satelliteSupport.value.toString() },
-                    { "Checked for system support. support=$str1" },
-                )
-
-                // We only need to check location availability if this mode is supported
-                if (satelliteSupport.value is Supported) {
-                    isSatelliteAllowedForCurrentLocation.subscriptionCount
-                        .map { it > 0 }
-                        .distinctUntilChanged()
-                        .collectLatest { hasSubscribers ->
-                            if (hasSubscribers) {
-                                /*
-                                 * As there is no listener available for checking satellite allowed,
-                                 * we must poll. Defaulting to polling at most once every hour while
-                                 * active. Subsequent OOS events will restart the job, so a flaky
-                                 * connection might cause more frequent checks.
-                                 */
-                                while (true) {
-                                    logBuffer.i {
-                                        "requestIsCommunicationAllowedForCurrentLocation"
-                                    }
-                                    checkIsSatelliteAllowed()
-                                    delay(POLLING_INTERVAL_MS)
-                                }
-                            }
-                        }
-                }
-            }
-        } else {
-            logBuffer.i { "Satellite manager is null" }
-
-            satelliteSupport.value = NotSupported
-        }
-    }
-
     /**
      * Note that we are given an "unbound" [TelephonyManager] (meaning it was not created with a
      * specific `subscriptionId`). Therefore this is the radio power state of the
@@ -267,6 +217,134 @@
             }
             .onStart { emit(Unit) }
 
+    init {
+        satelliteManager = satelliteManagerOpt.getOrNull()
+
+        isSatelliteAllowedForCurrentLocation = MutableStateFlow(false)
+
+        if (satelliteManager != null) {
+            // Outer scope launch allows us to delay until MIN_UPTIME
+            scope.launch {
+                // First, check that satellite is supported on this device
+                satelliteSupport.value = checkSatelliteSupportAfterMinUptime(satelliteManager)
+                logBuffer.i(
+                    { str1 = satelliteSupport.value.toString() },
+                    { "Checked for system support. support=$str1" },
+                )
+
+                // Second, launch a job to poll for service availability based on location
+                scope.launch { pollForAvailabilityBasedOnLocation() }
+
+                // Third, register a listener to let us know if there are changes to support
+                scope.launch { listenForChangesToSatelliteSupport(satelliteManager) }
+            }
+        } else {
+            logBuffer.i { "Satellite manager is null" }
+            satelliteSupport.value = NotSupported
+        }
+    }
+
+    private suspend fun checkSatelliteSupportAfterMinUptime(
+        sm: SatelliteManager
+    ): SatelliteSupport {
+        val waitTime = ensureMinUptime(systemClock, MIN_UPTIME)
+        if (waitTime > 0) {
+            logBuffer.i({ long1 = waitTime }) {
+                "Waiting $long1 ms before checking for satellite support"
+            }
+            delay(waitTime)
+        }
+
+        return sm.checkSatelliteSupported()
+    }
+
+    /*
+     * As there is no listener available for checking satellite allowed, we must poll the service.
+     * Defaulting to polling at most once every 20m while active. Subsequent OOS events will restart
+     * the job, so a flaky connection might cause more frequent checks.
+     */
+    private suspend fun pollForAvailabilityBasedOnLocation() {
+        satelliteSupport
+            .whenSupported(
+                supported = ::isSatelliteAllowedHasListener,
+                orElse = flowOf(false),
+                retrySignal = telephonyProcessCrashedEvent,
+            )
+            .collectLatest { hasSubscribers ->
+                if (hasSubscribers) {
+                    while (true) {
+                        logBuffer.i { "requestIsCommunicationAllowedForCurrentLocation" }
+                        checkIsSatelliteAllowed()
+                        delay(POLLING_INTERVAL_MS)
+                    }
+                }
+            }
+    }
+
+    /**
+     * Register a callback with [SatelliteManager] to let us know if there is a change in satellite
+     * support. This job restarts if there is a crash event detected.
+     *
+     * Note that the structure of this method looks similar to [whenSupported], but since we want
+     * this callback registered even when it is [NotSupported], we just mimic the structure here.
+     */
+    private suspend fun listenForChangesToSatelliteSupport(sm: SatelliteManager) {
+        telephonyProcessCrashedEvent.collectLatest {
+            satelliteIsSupportedCallback.collect { supported ->
+                if (supported) {
+                    satelliteSupport.value = Supported(sm)
+                } else {
+                    satelliteSupport.value = NotSupported
+                }
+            }
+        }
+    }
+
+    /**
+     * Callback version of [checkSatelliteSupported]. This flow should be retried on the same
+     * [telephonyProcessCrashedEvent] signal, but does not require a [SupportedSatelliteManager],
+     * since it specifically watches for satellite support.
+     */
+    private val satelliteIsSupportedCallback: Flow<Boolean> =
+        if (satelliteManager == null) {
+            flowOf(false)
+        } else {
+            conflatedCallbackFlow {
+                val callback = SatelliteSupportedStateCallback { supported ->
+                    logBuffer.i {
+                        "onSatelliteSupportedStateChanged: " +
+                            "${if (supported) "supported" else "not supported"}"
+                    }
+                    trySend(supported)
+                }
+
+                var registered = false
+                try {
+                    satelliteManager.registerForSupportedStateChanged(
+                        bgDispatcher.asExecutor(),
+                        callback
+                    )
+                    registered = true
+                } catch (e: Exception) {
+                    logBuffer.e("error registering for supported state change", e)
+                }
+
+                awaitClose {
+                    if (registered) {
+                        satelliteManager.unregisterForSupportedStateChanged(callback)
+                    }
+                }
+            }
+        }
+
+    /**
+     * Signal that we should start polling [checkIsSatelliteAllowed]. We only need to poll if there
+     * are active listeners to [isSatelliteAllowedForCurrentLocation]
+     */
+    @SuppressWarnings("unused")
+    private fun isSatelliteAllowedHasListener(sm: SupportedSatelliteManager): Flow<Boolean> =
+        isSatelliteAllowedForCurrentLocation.subscriptionCount.map { it > 0 }.distinctUntilChanged()
+
     override val connectionState =
         satelliteSupport
             .whenSupported(
@@ -310,7 +388,7 @@
     private fun signalStrengthFlow(sm: SupportedSatelliteManager) =
         conflatedCallbackFlow {
                 val cb = NtnSignalStrengthCallback { signalStrength ->
-                    logBuffer.i({ int1 = signalStrength.level }) {
+                    verboseLogBuffer.i({ int1 = signalStrength.level }) {
                         "onNtnSignalStrengthChanged: level=$int1"
                     }
                     trySend(signalStrength.level)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
index 5b954b2..b66ace6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt
@@ -21,7 +21,7 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.core.LogLevel
-import com.android.systemui.statusbar.pipeline.dagger.OemSatelliteInputLog
+import com.android.systemui.statusbar.pipeline.dagger.DeviceBasedSatelliteInputLog
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
 import com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelliteRepository
 import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
@@ -48,7 +48,7 @@
     deviceProvisioningInteractor: DeviceProvisioningInteractor,
     wifiInteractor: WifiInteractor,
     @Application scope: CoroutineScope,
-    @OemSatelliteInputLog private val logBuffer: LogBuffer,
+    @DeviceBasedSatelliteInputLog private val logBuffer: LogBuffer,
 ) {
     /** Must be observed by any UI showing Satellite iconography */
     val isSatelliteAllowed =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
index d76fd40..0ed1b9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt
@@ -24,7 +24,7 @@
 import com.android.systemui.log.core.LogLevel
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
-import com.android.systemui.statusbar.pipeline.dagger.OemSatelliteInputLog
+import com.android.systemui.statusbar.pipeline.dagger.DeviceBasedSatelliteInputLog
 import com.android.systemui.statusbar.pipeline.satellite.domain.interactor.DeviceBasedSatelliteInteractor
 import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
 import com.android.systemui.statusbar.pipeline.satellite.ui.model.SatelliteIconModel
@@ -70,7 +70,7 @@
     interactor: DeviceBasedSatelliteInteractor,
     @Application scope: CoroutineScope,
     airplaneModeRepository: AirplaneModeRepository,
-    @OemSatelliteInputLog logBuffer: LogBuffer,
+    @DeviceBasedSatelliteInputLog logBuffer: LogBuffer,
 ) : DeviceBasedSatelliteViewModel {
     private val shouldShowIcon: Flow<Boolean> =
         interactor.areAllConnectionsOutOfService.flatMapLatest { allOos ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
index 7d7f49b..a2ec1f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
@@ -19,11 +19,17 @@
 import android.animation.Animator
 import android.animation.AnimatorListenerAdapter
 import android.view.View
+import android.widget.ImageView
 import androidx.lifecycle.Lifecycle
 import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.Flags
+import com.android.systemui.common.ui.binder.IconViewBinder
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.binder.ChipChronometerBinder
+import com.android.systemui.statusbar.chips.ui.view.ChipChronometer
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel
 import javax.inject.Inject
@@ -75,6 +81,35 @@
                         }
                     }
                 }
+
+                if (Flags.statusBarScreenSharingChips()) {
+                    val chipView: View = view.requireViewById(R.id.ongoing_activity_chip)
+                    val chipIconView: ImageView =
+                        chipView.requireViewById(R.id.ongoing_activity_chip_icon)
+                    val chipTimeView: ChipChronometer =
+                        chipView.requireViewById(R.id.ongoing_activity_chip_time)
+                    launch {
+                        viewModel.ongoingActivityChip.collect { chipModel ->
+                            when (chipModel) {
+                                is OngoingActivityChipModel.Shown -> {
+                                    IconViewBinder.bind(chipModel.icon, chipIconView)
+                                    ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView)
+                                    // TODO(b/332662551): Attach click listener to chip
+
+                                    listener.onOngoingActivityStatusChanged(
+                                        hasOngoingActivity = true
+                                    )
+                                }
+                                is OngoingActivityChipModel.Hidden -> {
+                                    chipTimeView.stop()
+                                    listener.onOngoingActivityStatusChanged(
+                                        hasOngoingActivity = false
+                                    )
+                                }
+                            }
+                        }
+                    }
+                }
             }
         }
     }
@@ -120,4 +155,7 @@
 
     /** Called when a transition from lockscreen to dream has started. */
     fun onTransitionFromLockscreenToDreamStarted()
+
+    /** Called when the status of the ongoing activity chip (active or not active) has changed. */
+    fun onOngoingActivityStatusChanged(hasOngoingActivity: Boolean)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
index 0a6e95e..bb3a67e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
@@ -24,6 +24,8 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
 import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
 import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModel
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor
@@ -59,6 +61,9 @@
     /** Emits whenever a transition from lockscreen to dream has started. */
     val transitionFromLockscreenToDreamStartedEvent: Flow<Unit>
 
+    /** The ongoing activity chip that should be shown on the left-hand side of the status bar. */
+    val ongoingActivityChip: StateFlow<OngoingActivityChipModel>
+
     /**
      * Apps can request a low profile mode [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE] where
      * status bar and navigation icons dim. In this mode, a notification dot appears where the
@@ -78,6 +83,7 @@
     private val lightsOutInteractor: LightsOutInteractor,
     private val notificationsInteractor: ActiveNotificationsInteractor,
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
+    ongoingActivityChipsViewModel: OngoingActivityChipsViewModel,
     @Application coroutineScope: CoroutineScope,
 ) : CollapsedStatusBarViewModel {
     override val isTransitioningFromLockscreenToOccluded: StateFlow<Boolean> =
@@ -91,6 +97,8 @@
             .filter { it.transitionState == TransitionState.STARTED }
             .map {}
 
+    override val ongoingActivityChip = ongoingActivityChipsViewModel.chip
+
     override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> =
         if (NotificationsLiveDataStoreRefactor.isUnexpectedlyInLegacyMode()) {
             emptyFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
index 2169154..a972985 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
@@ -17,6 +17,8 @@
 
 import android.util.Log
 import androidx.annotation.VisibleForTesting
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
 import com.android.systemui.Dumpable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dump.DumpManager
@@ -35,6 +37,7 @@
 @Inject
 constructor(
     dumpManager: DumpManager,
+    private val uiEventLogger: UiEventLogger
 ) : Dumpable {
 
     private val tag = "AvalancheController"
@@ -65,6 +68,21 @@
     // For debugging only
     @VisibleForTesting var debugDropSet: MutableSet<HeadsUpEntry> = HashSet()
 
+    enum class ThrottleEvent(private val id: Int) : UiEventLogger.UiEventEnum {
+        @UiEvent(doc = "HUN was shown.")
+        SHOWN(1812),
+
+        @UiEvent(doc = "HUN was dropped to show higher priority HUNs.")
+        DROPPED(1813),
+
+        @UiEvent(doc = "HUN was removed while waiting to show.")
+        REMOVED(1814);
+
+        override fun getId(): Int {
+            return id
+        }
+    }
+
     init {
         dumpManager.registerNormalDumpable(tag, /* module */ this)
     }
@@ -145,6 +163,7 @@
             log { "$fn => remove from next" }
             if (entry in nextMap) nextMap.remove(entry)
             if (entry in nextList) nextList.remove(entry)
+            uiEventLogger.log(ThrottleEvent.REMOVED)
         } else if (entry in debugDropSet) {
             log { "$fn => remove from dropset" }
             debugDropSet.remove(entry)
@@ -254,6 +273,13 @@
         return null
     }
 
+    fun getWaitingEntryList(): List<HeadsUpEntry> {
+        if (!NotificationThrottleHun.isEnabled) {
+            return mutableListOf()
+        }
+        return nextMap.keys.toList()
+    }
+
     private fun isShowing(entry: HeadsUpEntry): Boolean {
         return headsUpEntryShowing != null && entry.mEntry?.key == headsUpEntryShowing?.mEntry?.key
     }
@@ -261,6 +287,7 @@
     private fun showNow(entry: HeadsUpEntry, runnableList: MutableList<Runnable>) {
         log { "SHOW: " + getKey(entry) }
 
+        uiEventLogger.log(ThrottleEvent.SHOWN)
         headsUpEntryShowing = entry
 
         runnableList.forEach {
@@ -288,6 +315,12 @@
 
         // Remove runnable labels for dropped huns
         val listToDrop = nextList.subList(1, nextList.size)
+
+        // Log dropped HUNs
+        for (e in listToDrop) {
+            uiEventLogger.log(ThrottleEvent.DROPPED)
+        }
+
         if (debug) {
             // Clear runnable labels
             for (e in listToDrop) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index 2ee98bb..fad5df8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -138,7 +138,7 @@
                 }
             }
         };
-        globalSettings.registerContentObserver(
+        globalSettings.registerContentObserverSync(
                 globalSettings.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS),
                 /* notifyForDescendants = */ false,
                 settingsObserver);
@@ -279,7 +279,6 @@
      * Returns the entry if it is managed by this manager.
      * @param key key of notification
      * @return the entry
-     * TODO(b/315362456) See if caller needs to check AvalancheController waiting entries
      */
     @Nullable
     public NotificationEntry getEntry(@NonNull String key) {
@@ -294,8 +293,13 @@
     @NonNull
     @Override
     public Stream<NotificationEntry> getAllEntries() {
-        // TODO(b/315362456) See if callers need to check AvalancheController
-        return mHeadsUpEntryMap.values().stream().map(headsUpEntry -> headsUpEntry.mEntry);
+        return getHeadsUpEntryList().stream().map(headsUpEntry -> headsUpEntry.mEntry);
+    }
+
+    public List<HeadsUpEntry> getHeadsUpEntryList() {
+        List<HeadsUpEntry> entryList = new ArrayList<>(mHeadsUpEntryMap.values());
+        entryList.addAll(mAvalancheController.getWaitingEntryList());
+        return entryList;
     }
 
     /**
@@ -304,7 +308,8 @@
      */
     @Override
     public boolean hasNotifications() {
-        return !mHeadsUpEntryMap.isEmpty();
+        return !mHeadsUpEntryMap.isEmpty()
+                || !mAvalancheController.getWaitingEntryList().isEmpty();
     }
 
     /**
@@ -507,8 +512,10 @@
 
     @Nullable
     protected HeadsUpEntry getHeadsUpEntry(@NonNull String key) {
-        // TODO(b/315362456) See if callers need to check AvalancheController
-        return mHeadsUpEntryMap.get(key);
+        if (mHeadsUpEntryMap.containsKey(key)) {
+            return mHeadsUpEntryMap.get(key);
+        }
+        return mAvalancheController.getWaitingEntry(key);
     }
 
     /**
@@ -688,8 +695,7 @@
      */
     @Override
     public boolean isSticky(String key) {
-        // TODO(b/315362456) See if callers need to check AvalancheController
-        HeadsUpEntry headsUpEntry = mHeadsUpEntryMap.get(key);
+        HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
         if (headsUpEntry != null) {
             return headsUpEntry.isSticky();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
index 8b63dfe..8123f8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
@@ -42,16 +42,16 @@
 import javax.inject.Inject
 
 @SysUISingleton
-open class DeviceProvisionedControllerImpl @Inject constructor(
+open class DeviceProvisionedControllerImpl
+@Inject
+constructor(
     private val secureSettings: SecureSettings,
     private val globalSettings: GlobalSettings,
     private val userTracker: UserTracker,
     private val dumpManager: DumpManager,
     @Background private val backgroundHandler: Handler,
     @Main private val mainExecutor: Executor
-) : DeviceProvisionedController,
-    DeviceProvisionedController.DeviceProvisionedListener,
-    Dumpable {
+) : DeviceProvisionedController, DeviceProvisionedController.DeviceProvisionedListener, Dumpable {
 
     companion object {
         private const val ALL_USERS = -1
@@ -63,8 +63,7 @@
     private val userSetupUri = secureSettings.getUriFor(Settings.Secure.USER_SETUP_COMPLETE)
 
     private val deviceProvisioned = AtomicBoolean(false)
-    @GuardedBy("lock")
-    private val userSetupComplete = SparseBooleanArray()
+    @GuardedBy("lock") private val userSetupComplete = SparseBooleanArray()
     @GuardedBy("lock")
     private val listeners = ArraySet<DeviceProvisionedController.DeviceProvisionedListener>()
 
@@ -81,42 +80,42 @@
         return _currentUser
     }
 
-    private val observer = object : ContentObserver(backgroundHandler) {
-        override fun onChange(
-            selfChange: Boolean,
-            uris: MutableCollection<Uri>,
-            flags: Int,
-            userId: Int
-        ) {
-            val updateDeviceProvisioned = deviceProvisionedUri in uris
-            val updateUser = if (userSetupUri in uris) userId else NO_USERS
-            updateValues(updateDeviceProvisioned, updateUser)
-            if (updateDeviceProvisioned) {
-                onDeviceProvisionedChanged()
-            }
-            if (updateUser != NO_USERS) {
-                onUserSetupChanged()
+    private val observer =
+        object : ContentObserver(backgroundHandler) {
+            override fun onChange(
+                selfChange: Boolean,
+                uris: MutableCollection<Uri>,
+                flags: Int,
+                userId: Int
+            ) {
+                val updateDeviceProvisioned = deviceProvisionedUri in uris
+                val updateUser = if (userSetupUri in uris) userId else NO_USERS
+                updateValues(updateDeviceProvisioned, updateUser)
+                if (updateDeviceProvisioned) {
+                    onDeviceProvisionedChanged()
+                }
+                if (updateUser != NO_USERS) {
+                    onUserSetupChanged()
+                }
             }
         }
-    }
 
-    private val userChangedCallback = object : UserTracker.Callback {
-        @WorkerThread
-        override fun onUserChanged(newUser: Int, userContext: Context) {
-            updateValues(updateDeviceProvisioned = false, updateUser = newUser)
-            onUserSwitched()
+    private val userChangedCallback =
+        object : UserTracker.Callback {
+            @WorkerThread
+            override fun onUserChanged(newUser: Int, userContext: Context) {
+                updateValues(updateDeviceProvisioned = false, updateUser = newUser)
+                onUserSwitched()
+            }
+
+            override fun onProfilesChanged(profiles: List<UserInfo>) {}
         }
 
-        override fun onProfilesChanged(profiles: List<UserInfo>) {}
-    }
-
     init {
         userSetupComplete.put(currentUser, false)
     }
 
-    /**
-     * Call to initialize values and register observers
-     */
+    /** Call to initialize values and register observers */
     open fun init() {
         if (!initted.compareAndSet(false, true)) {
             return
@@ -124,31 +123,39 @@
         dumpManager.registerDumpable(this)
         updateValues()
         userTracker.addCallback(userChangedCallback, backgroundExecutor)
-        globalSettings.registerContentObserver(deviceProvisionedUri, observer)
-        secureSettings.registerContentObserverForUser(userSetupUri, observer, UserHandle.USER_ALL)
+        globalSettings.registerContentObserverSync(deviceProvisionedUri, observer)
+        secureSettings.registerContentObserverForUserSync(
+            userSetupUri,
+            observer,
+            UserHandle.USER_ALL
+        )
     }
 
     @WorkerThread
-    private fun updateValues(
-            updateDeviceProvisioned: Boolean = true,
-            updateUser: Int = ALL_USERS
-    ) {
+    private fun updateValues(updateDeviceProvisioned: Boolean = true, updateUser: Int = ALL_USERS) {
         if (updateDeviceProvisioned) {
-            deviceProvisioned
-                    .set(globalSettings.getInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0)
+            deviceProvisioned.set(globalSettings.getInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0)
         }
         synchronized(lock) {
             if (updateUser == ALL_USERS) {
                 val n = userSetupComplete.size()
                 for (i in 0 until n) {
                     val user = userSetupComplete.keyAt(i)
-                    val value = secureSettings
-                            .getIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, user) != 0
+                    val value =
+                        secureSettings.getIntForUser(
+                            Settings.Secure.USER_SETUP_COMPLETE,
+                            0,
+                            user
+                        ) != 0
                     userSetupComplete.put(user, value)
                 }
             } else if (updateUser != NO_USERS) {
-                val value = secureSettings
-                        .getIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, updateUser) != 0
+                val value =
+                    secureSettings.getIntForUser(
+                        Settings.Secure.USER_SETUP_COMPLETE,
+                        0,
+                        updateUser
+                    ) != 0
                 userSetupComplete.put(updateUser, value)
             }
         }
@@ -160,15 +167,11 @@
      * The listener will not be called when this happens.
      */
     override fun addCallback(listener: DeviceProvisionedController.DeviceProvisionedListener) {
-        synchronized(lock) {
-            listeners.add(listener)
-        }
+        synchronized(lock) { listeners.add(listener) }
     }
 
     override fun removeCallback(listener: DeviceProvisionedController.DeviceProvisionedListener) {
-        synchronized(lock) {
-            listeners.remove(listener)
-        }
+        synchronized(lock) { listeners.remove(listener) }
     }
 
     override fun isDeviceProvisioned(): Boolean {
@@ -176,20 +179,14 @@
     }
 
     override fun isUserSetup(user: Int): Boolean {
-        val index = synchronized(lock) {
-            userSetupComplete.indexOfKey(user)
-        }
+        val index = synchronized(lock) { userSetupComplete.indexOfKey(user) }
         return if (index < 0) {
-            val value = secureSettings
-                    .getIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, user) != 0
-            synchronized(lock) {
-                userSetupComplete.put(user, value)
-            }
+            val value =
+                secureSettings.getIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, user) != 0
+            synchronized(lock) { userSetupComplete.put(user, value) }
             value
         } else {
-            synchronized(lock) {
-                userSetupComplete.get(user, false)
-            }
+            synchronized(lock) { userSetupComplete.get(user, false) }
         }
     }
 
@@ -214,12 +211,8 @@
     protected fun dispatchChange(
         callback: DeviceProvisionedController.DeviceProvisionedListener.() -> Unit
     ) {
-        val listenersCopy = synchronized(lock) {
-            ArrayList(listeners)
-        }
-        mainExecutor.execute {
-            listenersCopy.forEach(callback)
-        }
+        val listenersCopy = synchronized(lock) { ArrayList(listeners) }
+        mainExecutor.execute { listenersCopy.forEach(callback) }
     }
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
@@ -229,4 +222,4 @@
             pw.println("Listeners: $listeners")
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
index 58b82f1..da928a3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
@@ -32,6 +32,7 @@
 import androidx.annotation.NonNull;
 
 import com.android.internal.camera.flags.Flags;
+import com.android.systemui.util.ListenerSet;
 
 import java.util.Set;
 
@@ -43,7 +44,7 @@
     private final SparseBooleanArray mSoftwareToggleState = new SparseBooleanArray();
     private final SparseBooleanArray mHardwareToggleState = new SparseBooleanArray();
     private Boolean mRequiresAuthentication;
-    private final Set<Callback> mCallbacks = new ArraySet<>();
+    private final ListenerSet<Callback> mCallbacks = new ListenerSet<>();
 
     public IndividualSensorPrivacyControllerImpl(
             @NonNull SensorPrivacyManager sensorPrivacyManager) {
@@ -115,7 +116,7 @@
 
     @Override
     public void addCallback(@NonNull Callback listener) {
-        mCallbacks.add(listener);
+        mCallbacks.addIfAbsent(listener);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
index b07aa81..d210e93 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
@@ -18,13 +18,16 @@
 
 import android.app.IActivityTaskManager;
 
+import com.android.systemui.Dumpable;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.policy.KeyguardStateController.Callback;
 
+import java.io.PrintWriter;
+
 /**
  * Source of truth for keyguard state: If locked, occluded, has password, trusted etc.
  */
-public interface KeyguardStateController extends CallbackController<Callback> {
+public interface KeyguardStateController extends CallbackController<Callback>, Dumpable {
 
     /**
      * If the device is locked or unlocked.
@@ -40,6 +43,8 @@
         return isShowing() && !isOccluded();
     }
 
+    default void dump(PrintWriter pw, String[] args) { }
+
     /**
      * If the keyguard is showing. This includes when it's occluded by an activity, and when
      * the device is asleep or in always on mode, except when the screen timed out and the user
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 886010c..c256e64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -36,7 +36,6 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.keyguard.logging.KeyguardUpdateMonitorLogger;
-import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlags;
@@ -57,7 +56,7 @@
  *
  */
 @SysUISingleton
-public class KeyguardStateControllerImpl implements KeyguardStateController, Dumpable {
+public class KeyguardStateControllerImpl implements KeyguardStateController {
 
     private static final boolean DEBUG_AUTH_WITH_ADB = false;
     private static final String AUTH_BROADCAST_KEY = "debug_trigger_auth";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 9eee5d0..f57b696 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -115,7 +115,7 @@
         };
 
         // Register to listen for changes in Settings.Secure settings.
-        mSecureSettings.registerContentObserverForUser(
+        mSecureSettings.registerContentObserverForUserSync(
                 Settings.Secure.LOCATION_SHOW_SYSTEM_OPS, mContentObserver, UserHandle.USER_ALL);
 
         // Register to listen for changes in DeviceConfig settings.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java
index 40bb67f..9ab8175 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java
@@ -188,7 +188,7 @@
                 });
             }
         };
-        settings.registerContentObserver(
+        settings.registerContentObserverSync(
                 DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
                 developerOptionsObserver);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index 600005b..e09e577 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -140,13 +140,13 @@
         mNoMan = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
         if (registerZenModeContentObserverBackground()) {
             bgHandler.post(() -> {
-                globalSettings.registerContentObserver(Global.ZEN_MODE, modeContentObserver);
-                globalSettings.registerContentObserver(Global.ZEN_MODE_CONFIG_ETAG,
+                globalSettings.registerContentObserverSync(Global.ZEN_MODE, modeContentObserver);
+                globalSettings.registerContentObserverSync(Global.ZEN_MODE_CONFIG_ETAG,
                         configContentObserver);
             });
         } else {
-            globalSettings.registerContentObserver(Global.ZEN_MODE, modeContentObserver);
-            globalSettings.registerContentObserver(Global.ZEN_MODE_CONFIG_ETAG,
+            globalSettings.registerContentObserverSync(Global.ZEN_MODE, modeContentObserver);
+            globalSettings.registerContentObserverSync(Global.ZEN_MODE_CONFIG_ETAG,
                     configContentObserver);
         }
         updateZenMode(getModeSettingValueFromProvider());
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 7494649..4963aae 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -444,7 +444,7 @@
         filter.addAction(Intent.ACTION_WALLPAPER_CHANGED);
         mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, mMainExecutor,
                 UserHandle.ALL);
-        mSecureSettings.registerContentObserverForUser(
+        mSecureSettings.registerContentObserverForUserSync(
                 Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
                 false,
                 new ContentObserver(mBgHandler) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
deleted file mode 100644
index 2cad844..0000000
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.util.concurrency;
-
-import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
-
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Process;
-
-import com.android.systemui.Flags;
-import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.dagger.qualifiers.BroadcastRunning;
-import com.android.systemui.dagger.qualifiers.LongRunning;
-import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.dagger.qualifiers.NotifInflation;
-
-import dagger.Module;
-import dagger.Provides;
-
-import java.util.concurrent.Executor;
-
-import javax.inject.Named;
-
-/**
- * Dagger Module for classes found within the concurrent package.
- */
-@Module
-public abstract class SysUIConcurrencyModule {
-
-    // Slow BG executor can potentially affect UI if UI is waiting for an updated state from this
-    // thread
-    private static final Long BG_SLOW_DISPATCH_THRESHOLD = 1000L;
-    private static final Long BG_SLOW_DELIVERY_THRESHOLD = 1000L;
-    private static final Long LONG_SLOW_DISPATCH_THRESHOLD = 2500L;
-    private static final Long LONG_SLOW_DELIVERY_THRESHOLD = 2500L;
-    private static final Long BROADCAST_SLOW_DISPATCH_THRESHOLD = 1000L;
-    private static final Long BROADCAST_SLOW_DELIVERY_THRESHOLD = 1000L;
-    private static final Long NOTIFICATION_INFLATION_SLOW_DISPATCH_THRESHOLD = 1000L;
-    private static final Long NOTIFICATION_INFLATION_SLOW_DELIVERY_THRESHOLD = 1000L;
-
-    /** Background Looper */
-    @Provides
-    @SysUISingleton
-    @Background
-    public static Looper provideBgLooper() {
-        HandlerThread thread = new HandlerThread("SysUiBg",
-                Process.THREAD_PRIORITY_BACKGROUND);
-        thread.start();
-        thread.getLooper().setSlowLogThresholdMs(BG_SLOW_DISPATCH_THRESHOLD,
-                BG_SLOW_DELIVERY_THRESHOLD);
-        return thread.getLooper();
-    }
-
-    /** BroadcastRunning Looper (for sending and receiving broadcasts) */
-    @Provides
-    @SysUISingleton
-    @BroadcastRunning
-    public static Looper provideBroadcastRunningLooper() {
-        HandlerThread thread = new HandlerThread("BroadcastRunning",
-                Process.THREAD_PRIORITY_BACKGROUND);
-        thread.start();
-        thread.getLooper().setSlowLogThresholdMs(BROADCAST_SLOW_DISPATCH_THRESHOLD,
-                BROADCAST_SLOW_DELIVERY_THRESHOLD);
-        return thread.getLooper();
-    }
-
-    /** Long running tasks Looper */
-    @Provides
-    @SysUISingleton
-    @LongRunning
-    public static Looper provideLongRunningLooper() {
-        HandlerThread thread = new HandlerThread("SysUiLng",
-                Process.THREAD_PRIORITY_BACKGROUND);
-        thread.start();
-        thread.getLooper().setSlowLogThresholdMs(LONG_SLOW_DISPATCH_THRESHOLD,
-                LONG_SLOW_DELIVERY_THRESHOLD);
-        return thread.getLooper();
-    }
-
-    /** Notification inflation Looper */
-    @Provides
-    @SysUISingleton
-    @NotifInflation
-    public static Looper provideNotifInflationLooper(@Background Looper bgLooper) {
-        if (!Flags.dedicatedNotifInflationThread()) {
-            return bgLooper;
-        }
-
-        final HandlerThread thread = new HandlerThread("NotifInflation",
-                Process.THREAD_PRIORITY_BACKGROUND);
-        thread.start();
-        final Looper looper = thread.getLooper();
-        looper.setSlowLogThresholdMs(NOTIFICATION_INFLATION_SLOW_DISPATCH_THRESHOLD,
-                NOTIFICATION_INFLATION_SLOW_DELIVERY_THRESHOLD);
-        return looper;
-    }
-
-    /**
-     * Background Handler.
-     *
-     * Prefer the Background Executor when possible.
-     */
-    @Provides
-    @Background
-    public static Handler provideBgHandler(@Background Looper bgLooper) {
-        return new Handler(bgLooper);
-    }
-
-    /**
-     * Provide a BroadcastRunning Executor (for sending and receiving broadcasts).
-     */
-    @Provides
-    @SysUISingleton
-    @BroadcastRunning
-    public static Executor provideBroadcastRunningExecutor(@BroadcastRunning Looper looper) {
-        return new ExecutorImpl(looper);
-    }
-
-    /**
-     * Provide a Long running Executor.
-     */
-    @Provides
-    @SysUISingleton
-    @LongRunning
-    public static Executor provideLongRunningExecutor(@LongRunning Looper looper) {
-        return new ExecutorImpl(looper);
-    }
-
-    /**
-     * Provide a Long running Executor.
-     */
-    @Provides
-    @SysUISingleton
-    @LongRunning
-    public static DelayableExecutor provideLongRunningDelayableExecutor(
-            @LongRunning Looper looper) {
-        return new ExecutorImpl(looper);
-    }
-
-    /**
-     * Provide a Background-Thread Executor.
-     */
-    @Provides
-    @SysUISingleton
-    @Background
-    public static Executor provideBackgroundExecutor(@Background Looper looper) {
-        return new ExecutorImpl(looper);
-    }
-
-    /**
-     * Provide a Background-Thread Executor.
-     */
-    @Provides
-    @SysUISingleton
-    @Background
-    public static DelayableExecutor provideBackgroundDelayableExecutor(@Background Looper looper) {
-        return new ExecutorImpl(looper);
-    }
-
-    /**
-     * Provide a Background-Thread Executor.
-     */
-    @Provides
-    @SysUISingleton
-    @Background
-    public static RepeatableExecutor provideBackgroundRepeatableExecutor(
-            @Background DelayableExecutor exec) {
-        return new RepeatableExecutorImpl(exec);
-    }
-
-    /**
-     * Provide a Main-Thread Executor.
-     */
-    @Provides
-    @SysUISingleton
-    @Main
-    public static RepeatableExecutor provideMainRepeatableExecutor(@Main DelayableExecutor exec) {
-        return new RepeatableExecutorImpl(exec);
-    }
-
-    /** */
-    @Provides
-    @Main
-    public static MessageRouter providesMainMessageRouter(
-            @Main DelayableExecutor executor) {
-        return new MessageRouterImpl(executor);
-    }
-
-    /** */
-    @Provides
-    @Background
-    public static MessageRouter providesBackgroundMessageRouter(
-            @Background DelayableExecutor executor) {
-        return new MessageRouterImpl(executor);
-    }
-
-    /** */
-    @Provides
-    @SysUISingleton
-    @Named(TIME_TICK_HANDLER_NAME)
-    public static Handler provideTimeTickHandler() {
-        HandlerThread thread = new HandlerThread("TimeTick");
-        thread.start();
-        return new Handler(thread.getLooper());
-    }
-
-    /** */
-    @Provides
-    @SysUISingleton
-    @NotifInflation
-    public static Executor provideNotifInflationExecutor(@NotifInflation Looper looper) {
-        return new ExecutorImpl(looper);
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.kt b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.kt
new file mode 100644
index 0000000..a7abb6b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.kt
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.util.concurrency
+
+import android.os.Handler
+import android.os.HandlerThread
+import android.os.Looper
+import android.os.Process
+import android.view.Choreographer
+import com.android.systemui.Dependency
+import com.android.systemui.Flags
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.BroadcastRunning
+import com.android.systemui.dagger.qualifiers.LongRunning
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dagger.qualifiers.NotifInflation
+import dagger.Module
+import dagger.Provides
+import java.util.concurrent.Executor
+import javax.inject.Named
+import javax.inject.Qualifier
+
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class BackPanelUiThread
+
+/** Dagger Module for classes found within the concurrent package. */
+@Module
+object SysUIConcurrencyModule {
+    // Slow BG executor can potentially affect UI if UI is waiting for an updated state from this
+    // thread
+    private const val BG_SLOW_DISPATCH_THRESHOLD = 1000L
+    private const val BG_SLOW_DELIVERY_THRESHOLD = 1000L
+    private const val LONG_SLOW_DISPATCH_THRESHOLD = 2500L
+    private const val LONG_SLOW_DELIVERY_THRESHOLD = 2500L
+    private const val BROADCAST_SLOW_DISPATCH_THRESHOLD = 1000L
+    private const val BROADCAST_SLOW_DELIVERY_THRESHOLD = 1000L
+    private const val NOTIFICATION_INFLATION_SLOW_DISPATCH_THRESHOLD = 1000L
+    private const val NOTIFICATION_INFLATION_SLOW_DELIVERY_THRESHOLD = 1000L
+
+    /** Background Looper */
+    @Provides
+    @SysUISingleton
+    @Background
+    fun provideBgLooper(): Looper {
+        val thread = HandlerThread("SysUiBg", Process.THREAD_PRIORITY_BACKGROUND)
+        thread.start()
+        thread
+            .getLooper()
+            .setSlowLogThresholdMs(BG_SLOW_DISPATCH_THRESHOLD, BG_SLOW_DELIVERY_THRESHOLD)
+        return thread.getLooper()
+    }
+
+    /** BroadcastRunning Looper (for sending and receiving broadcasts) */
+    @Provides
+    @SysUISingleton
+    @BroadcastRunning
+    fun provideBroadcastRunningLooper(): Looper {
+        val thread = HandlerThread("BroadcastRunning", Process.THREAD_PRIORITY_BACKGROUND)
+        thread.start()
+        thread
+            .getLooper()
+            .setSlowLogThresholdMs(
+                BROADCAST_SLOW_DISPATCH_THRESHOLD,
+                BROADCAST_SLOW_DELIVERY_THRESHOLD
+            )
+        return thread.getLooper()
+    }
+
+    /** Long running tasks Looper */
+    @Provides
+    @SysUISingleton
+    @LongRunning
+    fun provideLongRunningLooper(): Looper {
+        val thread = HandlerThread("SysUiLng", Process.THREAD_PRIORITY_BACKGROUND)
+        thread.start()
+        thread
+            .getLooper()
+            .setSlowLogThresholdMs(LONG_SLOW_DISPATCH_THRESHOLD, LONG_SLOW_DELIVERY_THRESHOLD)
+        return thread.getLooper()
+    }
+
+    /** Notification inflation Looper */
+    @Provides
+    @SysUISingleton
+    @NotifInflation
+    fun provideNotifInflationLooper(@Background bgLooper: Looper): Looper {
+        if (!Flags.dedicatedNotifInflationThread()) {
+            return bgLooper
+        }
+        val thread = HandlerThread("NotifInflation", Process.THREAD_PRIORITY_BACKGROUND)
+        thread.start()
+        val looper = thread.getLooper()
+        looper.setSlowLogThresholdMs(
+            NOTIFICATION_INFLATION_SLOW_DISPATCH_THRESHOLD,
+            NOTIFICATION_INFLATION_SLOW_DELIVERY_THRESHOLD
+        )
+        return looper
+    }
+
+    @Provides
+    @SysUISingleton
+    @BackPanelUiThread
+    fun provideBackPanelUiThreadContext(
+        @Main mainLooper: Looper,
+        @Main mainHandler: Handler,
+        @Main mainExecutor: Executor
+    ): UiThreadContext {
+        return if (Flags.edgeBackGestureHandlerThread()) {
+            val thread =
+                HandlerThread("BackPanelUiThread", Process.THREAD_PRIORITY_DISPLAY).apply {
+                    start()
+                    looper.setSlowLogThresholdMs(
+                        LONG_SLOW_DISPATCH_THRESHOLD,
+                        LONG_SLOW_DELIVERY_THRESHOLD
+                    )
+                }
+            UiThreadContext(
+                thread.looper,
+                thread.threadHandler,
+                thread.threadExecutor,
+                thread.threadHandler.runWithScissors { Choreographer.getInstance() }
+            )
+        } else {
+            UiThreadContext(
+                mainLooper,
+                mainHandler,
+                mainExecutor,
+                mainHandler.runWithScissors { Choreographer.getInstance() }
+            )
+        }
+    }
+
+    /**
+     * Background Handler.
+     *
+     * Prefer the Background Executor when possible.
+     */
+    @Provides
+    @Background
+    fun provideBgHandler(@Background bgLooper: Looper): Handler = Handler(bgLooper)
+
+    /** Provide a BroadcastRunning Executor (for sending and receiving broadcasts). */
+    @Provides
+    @SysUISingleton
+    @BroadcastRunning
+    fun provideBroadcastRunningExecutor(@BroadcastRunning looper: Looper): Executor =
+        ExecutorImpl(looper)
+
+    /** Provide a Long running Executor. */
+    @Provides
+    @SysUISingleton
+    @LongRunning
+    fun provideLongRunningExecutor(@LongRunning looper: Looper): Executor = ExecutorImpl(looper)
+
+    /** Provide a Long running Executor. */
+    @Provides
+    @SysUISingleton
+    @LongRunning
+    fun provideLongRunningDelayableExecutor(@LongRunning looper: Looper): DelayableExecutor =
+        ExecutorImpl(looper)
+
+    /** Provide a Background-Thread Executor. */
+    @Provides
+    @SysUISingleton
+    @Background
+    fun provideBackgroundExecutor(@Background looper: Looper): Executor = ExecutorImpl(looper)
+
+    /** Provide a Background-Thread Executor. */
+    @Provides
+    @SysUISingleton
+    @Background
+    fun provideBackgroundDelayableExecutor(@Background looper: Looper): DelayableExecutor =
+        ExecutorImpl(looper)
+
+    /** Provide a Background-Thread Executor. */
+    @Provides
+    @SysUISingleton
+    @Background
+    fun provideBackgroundRepeatableExecutor(
+        @Background exec: DelayableExecutor
+    ): RepeatableExecutor = RepeatableExecutorImpl(exec)
+
+    /** Provide a Main-Thread Executor. */
+    @Provides
+    @SysUISingleton
+    @Main
+    fun provideMainRepeatableExecutor(@Main exec: DelayableExecutor): RepeatableExecutor =
+        RepeatableExecutorImpl(exec)
+
+    /**  */
+    @Provides
+    @Main
+    fun providesMainMessageRouter(@Main executor: DelayableExecutor): MessageRouter =
+        MessageRouterImpl(executor)
+
+    /**  */
+    @Provides
+    @Background
+    fun providesBackgroundMessageRouter(@Background executor: DelayableExecutor): MessageRouter =
+        MessageRouterImpl(executor)
+
+    /**  */
+    @Provides
+    @SysUISingleton
+    @Named(Dependency.TIME_TICK_HANDLER_NAME)
+    fun provideTimeTickHandler(): Handler {
+        val thread = HandlerThread("TimeTick")
+        thread.start()
+        return Handler(thread.getLooper())
+    }
+
+    /**  */
+    @Provides
+    @SysUISingleton
+    @NotifInflation
+    fun provideNotifInflationExecutor(@NotifInflation looper: Looper): Executor =
+        ExecutorImpl(looper)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/UiThreadContext.kt b/packages/SystemUI/src/com/android/systemui/util/concurrency/UiThreadContext.kt
new file mode 100644
index 0000000..8c8c686
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/UiThreadContext.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.concurrency
+
+import android.os.Handler
+import android.os.Looper
+import android.view.Choreographer
+import com.android.systemui.util.Assert
+import java.util.concurrent.Executor
+import java.util.concurrent.atomic.AtomicReference
+
+private const val DEFAULT_TIMEOUT = 150L
+
+class UiThreadContext(
+    val looper: Looper,
+    val handler: Handler,
+    val executor: Executor,
+    val choreographer: Choreographer
+) {
+    fun isCurrentThread() {
+        Assert.isCurrentThread(looper)
+    }
+
+    fun <T> runWithScissors(block: () -> T): T {
+        return handler.runWithScissors(block)
+    }
+
+    fun runWithScissors(block: Runnable) {
+        handler.runWithScissors(block, DEFAULT_TIMEOUT)
+    }
+}
+
+fun <T> Handler.runWithScissors(block: () -> T): T {
+    val returnedValue = AtomicReference<T>()
+    runWithScissors({ returnedValue.set(block()) }, DEFAULT_TIMEOUT)
+    return returnedValue.get()!!
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
index 1ec86a4..8c46f9a 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/JavaAdapter.kt
@@ -88,8 +88,8 @@
     flow: Flow<T>,
     consumer: Consumer<T>,
     state: Lifecycle.State = Lifecycle.State.CREATED,
-) {
-    lifecycle.coroutineScope.launch {
+): Job {
+    return lifecycle.coroutineScope.launch {
         lifecycle.repeatOnLifecycle(state) { flow.collect { consumer.accept(it) } }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
index ec89610..ed52233 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
@@ -37,7 +37,6 @@
 interface SettingsProxy {
     /** Returns the [ContentResolver] this instance was constructed with. */
     fun getContentResolver(): ContentResolver
-
     /**
      * Construct the content URI for a particular name/value pair, useful for monitoring changes
      * with a ContentObserver.
@@ -46,42 +45,36 @@
      * @return the corresponding content URI, or null if not present
      */
     fun getUriFor(name: String): Uri
-
     /**
      * Convenience wrapper around [ContentResolver.registerContentObserver].'
      *
      * Implicitly calls [getUriFor] on the passed in name.
      */
-    fun registerContentObserver(name: String, settingsObserver: ContentObserver) {
-        registerContentObserver(getUriFor(name), settingsObserver)
+    fun registerContentObserverSync(name: String, settingsObserver: ContentObserver) {
+        registerContentObserverSync(getUriFor(name), settingsObserver)
     }
-
     /** Convenience wrapper around [ContentResolver.registerContentObserver].' */
-    fun registerContentObserver(uri: Uri, settingsObserver: ContentObserver) =
-        registerContentObserver(uri, false, settingsObserver)
-
+    fun registerContentObserverSync(uri: Uri, settingsObserver: ContentObserver) =
+        registerContentObserverSync(uri, false, settingsObserver)
     /**
      * Convenience wrapper around [ContentResolver.registerContentObserver].'
      *
      * Implicitly calls [getUriFor] on the passed in name.
      */
-    fun registerContentObserver(
+    fun registerContentObserverSync(
         name: String,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver
-    ) = registerContentObserver(getUriFor(name), notifyForDescendants, settingsObserver)
-
+    ) = registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
     /** Convenience wrapper around [ContentResolver.registerContentObserver].' */
-    fun registerContentObserver(
+    fun registerContentObserverSync(
         uri: Uri,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver
     ) = getContentResolver().registerContentObserver(uri, notifyForDescendants, settingsObserver)
-
     /** See [ContentResolver.unregisterContentObserver]. */
-    fun unregisterContentObserver(settingsObserver: ContentObserver) =
+    fun unregisterContentObserverSync(settingsObserver: ContentObserver) =
         getContentResolver().unregisterContentObserver(settingsObserver)
-
     /**
      * Look up a name in the database.
      *
@@ -89,7 +82,6 @@
      * @return the corresponding value, or null if not present
      */
     fun getString(name: String): String
-
     /**
      * Store a name/value pair into the database.
      *
@@ -98,7 +90,6 @@
      * @return true if the value was set, false on database errors
      */
     fun putString(name: String, value: String): Boolean
-
     /**
      * Store a name/value pair into the database.
      *
@@ -129,7 +120,6 @@
      * @see .resetToDefaults
      */
     fun putString(name: String, value: String, tag: String, makeDefault: Boolean): Boolean
-
     /**
      * Convenience function for retrieving a single secure settings value as an integer. Note that
      * internally setting values are always stored as strings; this function converts the string to
@@ -148,7 +138,6 @@
             def
         }
     }
-
     /**
      * Convenience function for retrieving a single secure settings value as an integer. Note that
      * internally setting values are always stored as strings; this function converts the string to
@@ -171,7 +160,6 @@
             throw SettingNotFoundException(name)
         }
     }
-
     /**
      * Convenience function for updating a single settings value as an integer. This will either
      * create a new entry in the table if the given name does not exist, or modify the value of the
@@ -185,7 +173,6 @@
     fun putInt(name: String, value: Int): Boolean {
         return putString(name, value.toString())
     }
-
     /**
      * Convenience function for retrieving a single secure settings value as a boolean. Note that
      * internally setting values are always stored as strings; this function converts the string to
@@ -199,7 +186,6 @@
     fun getBool(name: String, def: Boolean): Boolean {
         return getInt(name, if (def) 1 else 0) != 0
     }
-
     /**
      * Convenience function for retrieving a single secure settings value as a boolean. Note that
      * internally setting values are always stored as strings; this function converts the string to
@@ -217,7 +203,6 @@
     fun getBool(name: String): Boolean {
         return getInt(name) != 0
     }
-
     /**
      * Convenience function for updating a single settings value as a boolean. This will either
      * create a new entry in the table if the given name does not exist, or modify the value of the
@@ -231,7 +216,6 @@
     fun putBool(name: String, value: Boolean): Boolean {
         return putInt(name, if (value) 1 else 0)
     }
-
     /**
      * Convenience function for retrieving a single secure settings value as a `long`. Note that
      * internally setting values are always stored as strings; this function converts the string to
@@ -246,7 +230,6 @@
         val valString = getString(name)
         return parseLongOrUseDefault(valString, def)
     }
-
     /**
      * Convenience function for retrieving a single secure settings value as a `long`. Note that
      * internally setting values are always stored as strings; this function converts the string to
@@ -265,7 +248,6 @@
         val valString = getString(name)
         return parseLongOrThrow(name, valString)
     }
-
     /**
      * Convenience function for updating a secure settings value as a long integer. This will either
      * create a new entry in the table if the given name does not exist, or modify the value of the
@@ -279,7 +261,6 @@
     fun putLong(name: String, value: Long): Boolean {
         return putString(name, value.toString())
     }
-
     /**
      * Convenience function for retrieving a single secure settings value as a floating point
      * number. Note that internally setting values are always stored as strings; this function
@@ -294,7 +275,6 @@
         val v = getString(name)
         return parseFloat(v, def)
     }
-
     /**
      * Convenience function for retrieving a single secure settings value as a float. Note that
      * internally setting values are always stored as strings; this function converts the string to
@@ -313,7 +293,6 @@
         val v = getString(name)
         return parseFloatOrThrow(name, v)
     }
-
     /**
      * Convenience function for updating a single settings value as a floating point number. This
      * will either create a new entry in the table if the given name does not exist, or modify the
@@ -327,7 +306,6 @@
     fun putFloat(name: String, value: Float): Boolean {
         return putString(name, value.toString())
     }
-
     companion object {
         /** Convert a string to a long, or uses a default if the string is malformed or null */
         @JvmStatic
@@ -341,7 +319,6 @@
                 }
             return value
         }
-
         /** Convert a string to a long, or throws an exception if the string is malformed or null */
         @JvmStatic
         @Throws(SettingNotFoundException::class)
@@ -355,7 +332,6 @@
                 throw SettingNotFoundException(name)
             }
         }
-
         /** Convert a string to a float, or uses a default if the string is malformed or null */
         @JvmStatic
         fun parseFloat(v: String?, def: Float): Float {
@@ -365,7 +341,6 @@
                 def
             }
         }
-
         /**
          * Convert a string to a float, or throws an exception if the string is malformed or null
          */
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt
index 7484368..d757e33 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt
@@ -39,9 +39,9 @@
                     }
                 }
 
-            names.forEach { name -> registerContentObserverForUser(name, observer, userId) }
+            names.forEach { name -> registerContentObserverForUserSync(name, observer, userId) }
 
-            awaitClose { unregisterContentObserver(observer) }
+            awaitClose { unregisterContentObserverSync(observer) }
         }
     }
 
@@ -57,9 +57,9 @@
                     }
                 }
 
-            names.forEach { name -> registerContentObserver(name, observer) }
+            names.forEach { name -> registerContentObserverSync(name, observer) }
 
-            awaitClose { unregisterContentObserver(observer) }
+            awaitClose { unregisterContentObserverSync(observer) }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
index 2285270..ed13943 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
@@ -41,10 +41,8 @@
  * instances, unifying setting related actions in one place.
  */
 interface UserSettingsProxy : SettingsProxy {
-
     /** Returns that [UserTracker] this instance was constructed with. */
     val userTracker: UserTracker
-
     /** Returns the user id for the associated [ContentResolver]. */
     var userId: Int
         get() = getContentResolver().userId
@@ -53,7 +51,6 @@
                 "userId cannot be set in interface, use setter from an implementation instead."
             )
         }
-
     /**
      * Returns the actual current user handle when querying with the current user. Otherwise,
      * returns the passed in user id.
@@ -63,63 +60,57 @@
             userHandle
         } else userTracker.userId
     }
-
-    override fun registerContentObserver(uri: Uri, settingsObserver: ContentObserver) {
-        registerContentObserverForUser(uri, settingsObserver, userId)
+    override fun registerContentObserverSync(uri: Uri, settingsObserver: ContentObserver) {
+        registerContentObserverForUserSync(uri, settingsObserver, userId)
     }
-
     /** Convenience wrapper around [ContentResolver.registerContentObserver].' */
-    override fun registerContentObserver(
+    override fun registerContentObserverSync(
         uri: Uri,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver
     ) {
-        registerContentObserverForUser(uri, notifyForDescendants, settingsObserver, userId)
+        registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId)
     }
-
     /**
      * Convenience wrapper around [ContentResolver.registerContentObserver]
      *
      * Implicitly calls [getUriFor] on the passed in name.
      */
-    fun registerContentObserverForUser(
+    fun registerContentObserverForUserSync(
         name: String,
         settingsObserver: ContentObserver,
         userHandle: Int
     ) {
-        registerContentObserverForUser(getUriFor(name), settingsObserver, userHandle)
+        registerContentObserverForUserSync(getUriFor(name), settingsObserver, userHandle)
     }
-
     /** Convenience wrapper around [ContentResolver.registerContentObserver] */
-    fun registerContentObserverForUser(
+    fun registerContentObserverForUserSync(
         uri: Uri,
         settingsObserver: ContentObserver,
         userHandle: Int
     ) {
-        registerContentObserverForUser(uri, false, settingsObserver, userHandle)
+        registerContentObserverForUserSync(uri, false, settingsObserver, userHandle)
     }
-
     /**
      * Convenience wrapper around [ContentResolver.registerContentObserver]
      *
      * Implicitly calls [getUriFor] on the passed in name.
      */
-    fun registerContentObserverForUser(
+    fun registerContentObserverForUserSync(
         name: String,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver,
         userHandle: Int
     ) {
-        registerContentObserverForUser(
+        registerContentObserverForUserSync(
             getUriFor(name),
             notifyForDescendants,
             settingsObserver,
             userHandle
         )
     }
-
     /** Convenience wrapper around [ContentResolver.registerContentObserver] */
-    fun registerContentObserverForUser(
+    fun registerContentObserverForUserSync(
         uri: Uri,
         notifyForDescendants: Boolean,
         settingsObserver: ContentObserver,
@@ -136,7 +127,6 @@
             Unit
         }
     }
-
     /**
      * Look up a name in the database.
      *
@@ -146,10 +136,8 @@
     override fun getString(name: String): String {
         return getStringForUser(name, userId)
     }
-
     /** See [getString]. */
     fun getStringForUser(name: String, userHandle: Int): String
-
     /**
      * Store a name/value pair into the database. Values written by this method will be overridden
      * if a restore happens in the future.
@@ -162,10 +150,8 @@
     override fun putString(name: String, value: String): Boolean {
         return putStringForUser(name, value, userId)
     }
-
     /** Similar implementation to [putString] for the specified [userHandle]. */
     fun putStringForUser(name: String, value: String, userHandle: Int): Boolean
-
     /** Similar implementation to [putString] for the specified [userHandle]. */
     fun putStringForUser(
         name: String,
@@ -175,11 +161,9 @@
         @UserIdInt userHandle: Int,
         overrideableByRestore: Boolean
     ): Boolean
-
     override fun getInt(name: String, def: Int): Int {
         return getIntForUser(name, def, userId)
     }
-
     /** Similar implementation to [getInt] for the specified [userHandle]. */
     fun getIntForUser(name: String, def: Int, userHandle: Int): Int {
         val v = getStringForUser(name, userHandle)
@@ -189,10 +173,8 @@
             def
         }
     }
-
     @Throws(SettingNotFoundException::class)
     override fun getInt(name: String) = getIntForUser(name, userId)
-
     /** Similar implementation to [getInt] for the specified [userHandle]. */
     @Throws(SettingNotFoundException::class)
     fun getIntForUser(name: String, userHandle: Int): Int {
@@ -203,66 +185,52 @@
             throw SettingNotFoundException(name)
         }
     }
-
     override fun putInt(name: String, value: Int) = putIntForUser(name, value, userId)
-
     /** Similar implementation to [getInt] for the specified [userHandle]. */
     fun putIntForUser(name: String, value: Int, userHandle: Int) =
         putStringForUser(name, value.toString(), userHandle)
-
     override fun getBool(name: String, def: Boolean) = getBoolForUser(name, def, userId)
-
     /** Similar implementation to [getBool] for the specified [userHandle]. */
     fun getBoolForUser(name: String, def: Boolean, userHandle: Int) =
         getIntForUser(name, if (def) 1 else 0, userHandle) != 0
-
     @Throws(SettingNotFoundException::class)
     override fun getBool(name: String) = getBoolForUser(name, userId)
-
     /** Similar implementation to [getBool] for the specified [userHandle]. */
     @Throws(SettingNotFoundException::class)
     fun getBoolForUser(name: String, userHandle: Int): Boolean {
         return getIntForUser(name, userHandle) != 0
     }
-
     override fun putBool(name: String, value: Boolean): Boolean {
         return putBoolForUser(name, value, userId)
     }
-
     /** Similar implementation to [putBool] for the specified [userHandle]. */
     fun putBoolForUser(name: String, value: Boolean, userHandle: Int) =
         putIntForUser(name, if (value) 1 else 0, userHandle)
-
     /** Similar implementation to [getLong] for the specified [userHandle]. */
     fun getLongForUser(name: String, def: Long, userHandle: Int): Long {
         val valString = getStringForUser(name, userHandle)
         return parseLongOrUseDefault(valString, def)
     }
-
     /** Similar implementation to [getLong] for the specified [userHandle]. */
     @Throws(SettingNotFoundException::class)
     fun getLongForUser(name: String, userHandle: Int): Long {
         val valString = getStringForUser(name, userHandle)
         return parseLongOrThrow(name, valString)
     }
-
     /** Similar implementation to [putLong] for the specified [userHandle]. */
     fun putLongForUser(name: String, value: Long, userHandle: Int) =
         putStringForUser(name, value.toString(), userHandle)
-
     /** Similar implementation to [getFloat] for the specified [userHandle]. */
     fun getFloatForUser(name: String, def: Float, userHandle: Int): Float {
         val v = getStringForUser(name, userHandle)
         return parseFloat(v, def)
     }
-
     /** Similar implementation to [getFloat] for the specified [userHandle]. */
     @Throws(SettingNotFoundException::class)
     fun getFloatForUser(name: String, userHandle: Int): Float {
         val v = getStringForUser(name, userHandle)
         return parseFloatOrThrow(name, v)
     }
-
     /** Similar implementation to [putFloat] for the specified [userHandle]. */
     fun putFloatForUser(name: String, value: Float, userHandle: Int) =
         putStringForUser(name, value.toString(), userHandle)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
index b6246da..154737c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
@@ -17,20 +17,20 @@
 package com.android.systemui.volume.domain.interactor
 
 import android.bluetooth.BluetoothAdapter
+import android.content.Context
 import android.media.AudioDeviceInfo
-import android.media.AudioDeviceInfo.TYPE_WIRED_HEADPHONES
-import android.media.AudioDeviceInfo.TYPE_WIRED_HEADSET
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
 import com.android.settingslib.bluetooth.LocalBluetoothManager
 import com.android.settingslib.media.BluetoothMediaDevice
 import com.android.settingslib.media.MediaDevice
 import com.android.settingslib.media.MediaDevice.MediaDeviceType
+import com.android.settingslib.media.PhoneMediaDevice
 import com.android.settingslib.volume.data.repository.AudioRepository
 import com.android.settingslib.volume.data.repository.AudioSharingRepository
 import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
+import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.volume.domain.model.AudioOutputDevice
-import com.android.systemui.volume.panel.component.mediaoutput.data.repository.LocalMediaRepositoryFactory
 import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
 import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
 import javax.inject.Inject
@@ -39,6 +39,7 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
@@ -50,6 +51,7 @@
 class AudioOutputInteractor
 @Inject
 constructor(
+    @Application private val context: Context,
     audioRepository: AudioRepository,
     audioModeInteractor: AudioModeInteractor,
     @VolumePanelScope scope: CoroutineScope,
@@ -58,11 +60,10 @@
     private val bluetoothAdapter: BluetoothAdapter?,
     private val deviceIconInteractor: DeviceIconInteractor,
     private val mediaOutputInteractor: MediaOutputInteractor,
-    private val localMediaRepositoryFactory: LocalMediaRepositoryFactory,
-    private val audioSharingRepository: AudioSharingRepository,
+    audioSharingRepository: AudioSharingRepository,
 ) {
 
-    val currentAudioDevice: Flow<AudioOutputDevice> =
+    val currentAudioDevice: StateFlow<AudioOutputDevice> =
         audioModeInteractor.isOngoingCall
             .flatMapLatest { isOngoingCall ->
                 if (isOngoingCall) {
@@ -83,30 +84,32 @@
     val isInAudioSharing: Flow<Boolean> = audioSharingRepository.inAudioSharing
 
     private fun AudioDeviceInfo.toAudioOutputDevice(): AudioOutputDevice {
-        if (type == TYPE_WIRED_HEADPHONES || type == TYPE_WIRED_HEADSET) {
+        if (
+            BluetoothAdapter.checkBluetoothAddress(address) &&
+                localBluetoothManager != null &&
+                bluetoothAdapter != null
+        ) {
+            val remoteDevice = bluetoothAdapter.getRemoteDevice(address)
+            localBluetoothManager.cachedDeviceManager.findDevice(remoteDevice)?.let {
+                device: CachedBluetoothDevice ->
+                return AudioOutputDevice.Bluetooth(
+                    name = device.name,
+                    icon = deviceIconInteractor.loadIcon(device),
+                    cachedBluetoothDevice = device,
+                )
+            }
+        }
+        // Built-in device has an empty address
+        if (address.isNotEmpty()) {
             return AudioOutputDevice.Wired(
                 name = productName.toString(),
                 icon = deviceIconInteractor.loadIcon(type),
             )
         }
-        val cachedBluetoothDevice: CachedBluetoothDevice? =
-            if (address.isEmpty() || localBluetoothManager == null || bluetoothAdapter == null) {
-                null
-            } else {
-                val remoteDevice = bluetoothAdapter.getRemoteDevice(address)
-                localBluetoothManager.cachedDeviceManager.findDevice(remoteDevice)
-            }
-        return cachedBluetoothDevice?.let {
-            AudioOutputDevice.Bluetooth(
-                name = it.name,
-                icon = deviceIconInteractor.loadIcon(it),
-                cachedBluetoothDevice = it,
-            )
-        }
-            ?: AudioOutputDevice.BuiltIn(
-                name = productName.toString(),
-                icon = deviceIconInteractor.loadIcon(type),
-            )
+        return AudioOutputDevice.BuiltIn(
+            name = PhoneMediaDevice.getMediaTransferThisDeviceName(context),
+            icon = deviceIconInteractor.loadIcon(type),
+        )
     }
 
     private fun MediaDevice.toAudioOutputDevice(): AudioOutputDevice {
@@ -117,7 +120,8 @@
                     icon = icon,
                     cachedBluetoothDevice = cachedDevice,
                 )
-            deviceType == MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE ->
+            deviceType == MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE ||
+                deviceType == MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE ->
                 AudioOutputDevice.Wired(
                     name = name,
                     icon = icon,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
index 0dc2647..79a4ae7 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/LocalMediaRepositoryFactory.kt
@@ -19,14 +19,13 @@
 import com.android.settingslib.volume.data.repository.LocalMediaRepositoryImpl
 import com.android.settingslib.volume.shared.AudioManagerEventsReceiver
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.media.controls.util.LocalMediaManagerFactory
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 
 interface LocalMediaRepositoryFactory {
 
-    fun create(packageName: String?): LocalMediaRepository
+    fun create(packageName: String?, coroutineScope: CoroutineScope): LocalMediaRepository
 }
 
 @SysUISingleton
@@ -35,10 +34,12 @@
 constructor(
     private val eventsReceiver: AudioManagerEventsReceiver,
     private val localMediaManagerFactory: LocalMediaManagerFactory,
-    @Application private val coroutineScope: CoroutineScope,
 ) : LocalMediaRepositoryFactory {
 
-    override fun create(packageName: String?): LocalMediaRepository =
+    override fun create(
+        packageName: String?,
+        coroutineScope: CoroutineScope
+    ): LocalMediaRepository =
         LocalMediaRepositoryImpl(
             eventsReceiver,
             localMediaManagerFactory.create(packageName),
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt
index 199bc3b..333f4ad 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt
@@ -21,7 +21,7 @@
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.animation.Expandable
 import com.android.systemui.media.dialog.MediaOutputDialogManager
-import com.android.systemui.volume.panel.component.mediaoutput.shared.model.SessionWithPlaybackState
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaOutputComponentModel
 import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
 import javax.inject.Inject
 
@@ -29,14 +29,12 @@
 @VolumePanelScope
 class MediaOutputActionsInteractor
 @Inject
-constructor(
-    private val mediaOutputDialogManager: MediaOutputDialogManager,
-) {
+constructor(private val mediaOutputDialogManager: MediaOutputDialogManager) {
 
-    fun onBarClick(sessionWithPlaybackState: SessionWithPlaybackState?, expandable: Expandable?) {
-        if (sessionWithPlaybackState?.isPlaybackActive == true) {
+    fun onBarClick(model: MediaOutputComponentModel?, expandable: Expandable?) {
+        if (model is MediaOutputComponentModel.MediaSession) {
             mediaOutputDialogManager.createAndShowWithController(
-                sessionWithPlaybackState.session.packageName,
+                model.session.packageName,
                 false,
                 expandable?.dialogController()
             )
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt
new file mode 100644
index 0000000..ed25129
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
+
+import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
+import com.android.systemui.volume.domain.interactor.AudioOutputInteractor
+import com.android.systemui.volume.domain.model.AudioOutputDevice
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaOutputComponentModel
+import com.android.systemui.volume.panel.component.mediaoutput.shared.model.SessionWithPlaybackState
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.shared.model.Result
+import com.android.systemui.volume.panel.shared.model.filterData
+import com.android.systemui.volume.panel.shared.model.wrapInResult
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.stateIn
+
+/** Gathers together a domain state for the Media Output Volume Panel component. */
+@OptIn(ExperimentalCoroutinesApi::class)
+@VolumePanelScope
+class MediaOutputComponentInteractor
+@Inject
+constructor(
+    @VolumePanelScope private val coroutineScope: CoroutineScope,
+    private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
+    audioOutputInteractor: AudioOutputInteractor,
+    audioModeInteractor: AudioModeInteractor,
+    interactor: MediaOutputInteractor,
+) {
+
+    private val sessionWithPlaybackState: StateFlow<Result<SessionWithPlaybackState?>> =
+        interactor.defaultActiveMediaSession
+            .filterData()
+            .flatMapLatest { session ->
+                if (session == null) {
+                    flowOf(null)
+                } else {
+                    mediaDeviceSessionInteractor.playbackState(session).mapNotNull { playback ->
+                        playback?.let { SessionWithPlaybackState(session, playback.isActive) }
+                    }
+                }
+            }
+            .wrapInResult()
+            .stateIn(
+                coroutineScope,
+                SharingStarted.Eagerly,
+                Result.Loading(),
+            )
+
+    private val currentAudioDevice: Flow<AudioOutputDevice> =
+        audioOutputInteractor.currentAudioDevice.filter { it !is AudioOutputDevice.Unknown }
+
+    val mediaOutputModel: StateFlow<Result<MediaOutputComponentModel>> =
+        audioModeInteractor.isOngoingCall
+            .flatMapLatest { isOngoingCall ->
+                audioOutputInteractor.isInAudioSharing.flatMapLatest { isInAudioSharing ->
+                    if (isOngoingCall) {
+                        currentAudioDevice.map {
+                            MediaOutputComponentModel.Calling(it, isInAudioSharing)
+                        }
+                    } else {
+                        combine(sessionWithPlaybackState.filterData(), currentAudioDevice) {
+                            sessionWithPlaybackState,
+                            currentAudioDevice ->
+                            if (sessionWithPlaybackState == null) {
+                                MediaOutputComponentModel.Idle(currentAudioDevice, isInAudioSharing)
+                            } else {
+                                MediaOutputComponentModel.MediaSession(
+                                    sessionWithPlaybackState.session,
+                                    sessionWithPlaybackState.isPlaybackActive,
+                                    currentAudioDevice,
+                                    isInAudioSharing,
+                                )
+                            }
+                        }
+                    }
+                }
+            }
+            .wrapInResult()
+            .stateIn(coroutineScope, SharingStarted.Eagerly, Result.Loading())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
index 9fbd79a..31a8977 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputInteractor.kt
@@ -35,6 +35,7 @@
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
@@ -46,6 +47,7 @@
 import kotlinx.coroutines.flow.merge
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.flow.transformLatest
 import kotlinx.coroutines.withContext
 
 /** Provides observable models about the current media session state. */
@@ -105,12 +107,9 @@
             .filterData()
             .map { it?.packageName }
             .distinctUntilChanged()
-            .map { localMediaRepositoryFactory.create(it) }
-            .stateIn(
-                coroutineScope,
-                SharingStarted.Eagerly,
-                localMediaRepositoryFactory.create(null)
-            )
+            .transformLatest {
+                coroutineScope { emit(localMediaRepositoryFactory.create(it, this)) }
+            }
 
     /** Currently connected [MediaDevice]. */
     val currentConnectedDevice: Flow<MediaDevice?> =
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaOutputComponentModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaOutputComponentModel.kt
new file mode 100644
index 0000000..220fb2b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaOutputComponentModel.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.model
+
+import com.android.systemui.volume.domain.model.AudioOutputDevice
+import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
+
+/** Models domain data for the Media Output Component */
+sealed interface MediaOutputComponentModel {
+
+    val device: AudioOutputDevice
+    val isInAudioSharing: Boolean
+
+    /** There is an ongoing call on the device. */
+    data class Calling(
+        override val device: AudioOutputDevice,
+        override val isInAudioSharing: Boolean,
+    ) : MediaOutputComponentModel
+
+    /** There is media playing on the device. */
+    data class MediaSession(
+        val session: MediaDeviceSession,
+        val isPlaybackActive: Boolean,
+        override val device: AudioOutputDevice,
+        override val isInAudioSharing: Boolean,
+    ) : MediaOutputComponentModel
+
+    /** There is nothing playing on the device. */
+    data class Idle(
+        override val device: AudioOutputDevice,
+        override val isInAudioSharing: Boolean,
+    ) : MediaOutputComponentModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
index 40b7977..36b42f2 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
@@ -18,33 +18,24 @@
 
 import android.content.Context
 import com.android.internal.logging.UiEventLogger
-import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
 import com.android.systemui.animation.Expandable
 import com.android.systemui.common.shared.model.Color
 import com.android.systemui.common.shared.model.Icon
 import com.android.systemui.res.R
-import com.android.systemui.volume.domain.interactor.AudioOutputInteractor
 import com.android.systemui.volume.domain.model.AudioOutputDevice
-import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaDeviceSessionInteractor
 import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputActionsInteractor
-import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
-import com.android.systemui.volume.panel.component.mediaoutput.shared.model.SessionWithPlaybackState
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputComponentInteractor
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaOutputComponentModel
 import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
 import com.android.systemui.volume.panel.shared.model.Result
 import com.android.systemui.volume.panel.shared.model.filterData
-import com.android.systemui.volume.panel.shared.model.wrapInResult
 import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.flow.stateIn
 
 /** Models the UI of the Media Output Volume Panel component. */
@@ -56,61 +47,40 @@
     private val context: Context,
     @VolumePanelScope private val coroutineScope: CoroutineScope,
     private val actionsInteractor: MediaOutputActionsInteractor,
-    private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
-    private val audioOutputInteractor: AudioOutputInteractor,
-    audioModeInteractor: AudioModeInteractor,
-    interactor: MediaOutputInteractor,
+    private val mediaOutputComponentInteractor: MediaOutputComponentInteractor,
     private val uiEventLogger: UiEventLogger,
 ) {
 
-    private val sessionWithPlaybackState: StateFlow<Result<SessionWithPlaybackState?>> =
-        interactor.defaultActiveMediaSession
-            .filterData()
-            .flatMapLatest { session ->
-                if (session == null) {
-                    flowOf(null)
-                } else {
-                    mediaDeviceSessionInteractor.playbackState(session).mapNotNull { playback ->
-                        playback?.let { SessionWithPlaybackState(session, playback.isActive) }
-                    }
-                }
-            }
-            .wrapInResult()
-            .stateIn(
-                coroutineScope,
-                SharingStarted.Eagerly,
-                Result.Loading(),
-            )
-
     val connectedDeviceViewModel: StateFlow<ConnectedDeviceViewModel?> =
-        combine(
-                sessionWithPlaybackState.filterData(),
-                audioModeInteractor.isOngoingCall,
-                audioOutputInteractor.currentAudioDevice.filter {
-                    it !is AudioOutputDevice.Unknown
-                },
-                audioOutputInteractor.isInAudioSharing,
-            ) { mediaDeviceSession, isOngoingCall, currentConnectedDevice, isInAudioSharing ->
+        mediaOutputComponentInteractor.mediaOutputModel
+            .filterData()
+            .map { mediaOutputModel ->
                 val label =
-                    when {
-                        isOngoingCall -> context.getString(R.string.media_output_title_ongoing_call)
-                        mediaDeviceSession?.isPlaybackActive == true ->
-                            context.getString(
-                                R.string.media_output_label_title,
-                                mediaDeviceSession.session.appLabel
-                            )
-                        else -> context.getString(R.string.media_output_title_without_playing)
+                    when (mediaOutputModel) {
+                        is MediaOutputComponentModel.Idle -> {
+                            context.getString(R.string.media_output_title_without_playing)
+                        }
+                        is MediaOutputComponentModel.MediaSession -> {
+                            if (mediaOutputModel.isPlaybackActive) {
+                                context.getString(
+                                    R.string.media_output_label_title,
+                                    mediaOutputModel.session.appLabel,
+                                )
+                            } else {
+                                context.getString(R.string.media_output_title_without_playing)
+                            }
+                        }
+                        is MediaOutputComponentModel.Calling -> {
+                            context.getString(R.string.media_output_title_ongoing_call)
+                        }
                     }
                 ConnectedDeviceViewModel(
                     label,
-                    when (isInAudioSharing) {
-                        true -> {
-                            context.getString(R.string.audio_sharing_description)
-                        }
-                        false -> {
-                            currentConnectedDevice.name
-                        }
-                    }
+                    if (mediaOutputModel.isInAudioSharing) {
+                        context.getString(R.string.audio_sharing_description)
+                    } else {
+                        mediaOutputModel.device.name
+                    },
                 )
             }
             .stateIn(
@@ -120,16 +90,20 @@
             )
 
     val deviceIconViewModel: StateFlow<DeviceIconViewModel?> =
-        combine(sessionWithPlaybackState.filterData(), audioOutputInteractor.currentAudioDevice) {
-                mediaDeviceSession,
-                currentConnectedDevice ->
+        mediaOutputComponentInteractor.mediaOutputModel
+            .filterData()
+            .map { mediaOutputModel ->
                 val icon: Icon =
-                    currentConnectedDevice
-                        .takeIf { currentConnectedDevice !is AudioOutputDevice.Unknown }
+                    mediaOutputModel.device
+                        .takeIf { it !is AudioOutputDevice.Unknown }
                         ?.icon
                         ?.let { Icon.Loaded(it, null) }
                         ?: Icon.Resource(R.drawable.ic_media_home_devices, null)
-                if (mediaDeviceSession?.isPlaybackActive == true) {
+                val isPlaybackActive =
+                    (mediaOutputModel as? MediaOutputComponentModel.MediaSession)
+                        ?.isPlaybackActive == true
+                val isCalling = mediaOutputModel is MediaOutputComponentModel.Calling
+                if (isPlaybackActive || isCalling) {
                     DeviceIconViewModel.IsPlaying(
                         icon = icon,
                         iconColor =
@@ -156,8 +130,9 @@
             )
 
     val enabled: StateFlow<Boolean> =
-        audioOutputInteractor.isInAudioSharing
-            .map { !it }
+        mediaOutputComponentInteractor.mediaOutputModel
+            .filterData()
+            .map { !it.isInAudioSharing }
             .stateIn(
                 coroutineScope,
                 SharingStarted.Eagerly,
@@ -166,7 +141,11 @@
 
     fun onBarClick(expandable: Expandable?) {
         uiEventLogger.log(VolumePanelUiEvent.VOLUME_PANEL_MEDIA_OUTPUT_CLICKED)
-        val result = sessionWithPlaybackState.value
-        actionsInteractor.onBarClick((result as? Result.Data)?.data, expandable)
+        val result: Result<MediaOutputComponentModel> =
+            mediaOutputComponentInteractor.mediaOutputModel.value
+        actionsInteractor.onBarClick(
+            (result as? Result.Data<MediaOutputComponentModel>)?.data,
+            expandable
+        )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
index 4b2d26a..6c6a1cc 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
@@ -48,15 +48,31 @@
     private val uiEventLogger: UiEventLogger,
 ) {
 
+    private val spatialSpeakerIcon =
+        Icon.Resource(R.drawable.ic_spatial_speaker, contentDescription = null)
+
     val spatialAudioButton: StateFlow<ButtonViewModel?> =
-        interactor.isEnabled
-            .map {
-                val isChecked = it is SpatialAudioEnabledModel.SpatialAudioEnabled
-                it.toViewModel(isChecked)
+        combine(interactor.isEnabled, interactor.isAvailable) { isEnabled, isAvailable ->
+                isEnabled
+                    .toViewModel(
+                        isChecked = isEnabled is SpatialAudioEnabledModel.SpatialAudioEnabled,
+                        isHeadTrackingAvailable =
+                            isAvailable is SpatialAudioAvailabilityModel.SpatialAudio,
+                    )
                     .copy(label = context.getString(R.string.volume_panel_spatial_audio_title))
             }
             .stateIn(scope, SharingStarted.Eagerly, null)
 
+    val shouldUsePopup: StateFlow<Boolean> =
+        interactor.isAvailable
+            .map {
+                // head tracking availability means there are three possible states for the spatial
+                // audio: disabled, enabled regular, enabled with head tracking.
+                // Show popup in this case instead of a togglealbe button.
+                it is SpatialAudioAvailabilityModel.SpatialAudio
+            }
+            .stateIn(scope, SharingStarted.Eagerly, false)
+
     val isAvailable: StateFlow<Boolean> =
         availabilityCriteria.isAvailable().stateIn(scope, SharingStarted.Eagerly, true)
 
@@ -73,8 +89,12 @@
                         }
                     }
                     .map { isEnabled ->
-                        val isChecked = isEnabled == currentIsEnabled
-                        val buttonViewModel: ButtonViewModel = isEnabled.toViewModel(isChecked)
+                        val buttonViewModel: ButtonViewModel =
+                            isEnabled.toViewModel(
+                                isChecked = isEnabled == currentIsEnabled,
+                                isHeadTrackingAvailable =
+                                    isAvailable is SpatialAudioAvailabilityModel.HeadTracking,
+                            )
                         SpatialAudioButtonViewModel(button = buttonViewModel, model = isEnabled)
                     }
             }
@@ -97,11 +117,21 @@
         scope.launch { interactor.setEnabled(model) }
     }
 
-    private fun SpatialAudioEnabledModel.toViewModel(isChecked: Boolean): ButtonViewModel {
+    private fun SpatialAudioEnabledModel.toViewModel(
+        isChecked: Boolean,
+        isHeadTrackingAvailable: Boolean,
+    ): ButtonViewModel {
+        // This method deliberately uses the same icon for the case when head tracking is disabled
+        // to show a toggle button with a non-changing icon
         if (this is SpatialAudioEnabledModel.HeadTrackingEnabled) {
             return ButtonViewModel(
                 isActive = isChecked,
-                icon = Icon.Resource(R.drawable.ic_head_tracking, contentDescription = null),
+                icon =
+                    if (isHeadTrackingAvailable) {
+                        Icon.Resource(R.drawable.ic_head_tracking, contentDescription = null)
+                    } else {
+                        spatialSpeakerIcon
+                    },
                 label = context.getString(R.string.volume_panel_spatial_audio_tracking)
             )
         }
@@ -109,7 +139,12 @@
         if (this is SpatialAudioEnabledModel.SpatialAudioEnabled) {
             return ButtonViewModel(
                 isActive = isChecked,
-                icon = Icon.Resource(R.drawable.ic_spatial_audio, contentDescription = null),
+                icon =
+                    if (isHeadTrackingAvailable) {
+                        Icon.Resource(R.drawable.ic_spatial_audio, contentDescription = null)
+                    } else {
+                        spatialSpeakerIcon
+                    },
                 label = context.getString(R.string.volume_panel_spatial_audio_fixed)
             )
         }
@@ -117,7 +152,12 @@
         if (this is SpatialAudioEnabledModel.Disabled) {
             return ButtonViewModel(
                 isActive = isChecked,
-                icon = Icon.Resource(R.drawable.ic_spatial_audio_off, contentDescription = null),
+                icon =
+                    if (isHeadTrackingAvailable) {
+                        Icon.Resource(R.drawable.ic_spatial_audio_off, contentDescription = null)
+                    } else {
+                        spatialSpeakerIcon
+                    },
                 label = context.getString(R.string.volume_panel_spatial_audio_off)
             )
         }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
index fd01b48..850162e 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
@@ -146,14 +146,18 @@
             isEnabled = isEnabled,
             a11yStep = volumeRange.step,
             a11yClickDescription =
-                context.getString(
-                    if (isMuted) {
-                        R.string.volume_panel_hint_unmute
-                    } else {
-                        R.string.volume_panel_hint_mute
-                    },
-                    label,
-                ),
+                if (isAffectedByMute) {
+                    context.getString(
+                        if (isMuted) {
+                            R.string.volume_panel_hint_unmute
+                        } else {
+                            R.string.volume_panel_hint_mute
+                        },
+                        label,
+                    )
+                } else {
+                    null
+                },
             a11yStateDescription =
                 if (volume == volumeRange.first) {
                     context.getString(
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt b/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt
index 03c8af90..3da725b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt
@@ -97,7 +97,14 @@
     }
 
     private fun showNewVolumePanel() {
-        volumePanelGlobalStateInteractor.setVisible(true)
+        activityStarter.dismissKeyguardThenExecute(
+            /* action = */ {
+                volumePanelGlobalStateInteractor.setVisible(true)
+                false
+            },
+            /* cancel = */ {},
+            /* afterKeyguardGone = */ true,
+        )
     }
 
     private fun createNewVolumePanelDialog(): Dialog {
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
index ff18418..1d32a4f 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
@@ -149,12 +149,12 @@
             if (event == WALLET_PREFERENCE_CHANGE && mWalletPreferenceObserver != null) {
                 mWalletPreferenceChangeEvents--;
                 if (mWalletPreferenceChangeEvents == 0) {
-                    mSecureSettings.unregisterContentObserver(mWalletPreferenceObserver);
+                    mSecureSettings.unregisterContentObserverSync(mWalletPreferenceObserver);
                 }
             } else if (event == DEFAULT_PAYMENT_APP_CHANGE && mDefaultPaymentAppObserver != null) {
                 mDefaultPaymentAppChangeEvents--;
                 if (mDefaultPaymentAppChangeEvents == 0) {
-                    mSecureSettings.unregisterContentObserver(mDefaultPaymentAppObserver);
+                    mSecureSettings.unregisterContentObserverSync(mDefaultPaymentAppObserver);
                 }
             } else if (event == DEFAULT_WALLET_APP_CHANGE && mDefaultWalletAppObserver != null) {
                 mDefaultWalletAppChangeEvents--;
@@ -312,7 +312,7 @@
                 }
             };
 
-            mSecureSettings.registerContentObserverForUser(
+            mSecureSettings.registerContentObserverForUserSync(
                     Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT,
                     false /* notifyForDescendants */,
                     mDefaultPaymentAppObserver,
@@ -351,7 +351,7 @@
                 }
             };
 
-            mSecureSettings.registerContentObserverForUser(
+            mSecureSettings.registerContentObserverForUserSync(
                     QuickAccessWalletClientImpl.SETTING_KEY,
                     false /* notifyForDescendants */,
                     mWalletPreferenceObserver,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
index 8c4179d..0a3225e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/CarrierTextManagerTest.java
@@ -161,8 +161,11 @@
         doAnswer(this::checkMainThread).when(mKeyguardUpdateMonitor)
                 .removeCallback(any(KeyguardUpdateMonitorCallback.class));
 
-        mCarrierTextCallbackInfo = new CarrierTextManager.CarrierTextCallbackInfo("",
-                new CharSequence[]{}, false, new int[]{});
+        mCarrierTextCallbackInfo = new CarrierTextManager.CarrierTextCallbackInfo(
+                /* carrierText= */ "",
+                /* listOfCarriers= */ new CharSequence[]{},
+                /* anySimReady= */ false,
+                /* subscriptionIds= */ new int[]{});
         when(mTelephonyManager.getSupportedModemCount()).thenReturn(3);
         when(mTelephonyManager.getActiveModemCount()).thenReturn(3);
 
@@ -473,7 +476,7 @@
     }
 
     @Test
-    public void carrierText_satelliteTextNull_notUsed() {
+    public void carrierText_satelliteTextNull_isSatelliteFalse_textNotUsed() {
         reset(mCarrierTextCallback);
         List<SubscriptionInfo> list = new ArrayList<>();
         list.add(TEST_SUBSCRIPTION);
@@ -491,12 +494,38 @@
                         CarrierTextManager.CarrierTextCallbackInfo.class);
         FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
 
-        // THEN the default subscription carrier text is used
+        // THEN satellite mode is false and the default subscription carrier text is used
         verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
+        assertThat(captor.getValue().isInSatelliteMode).isFalse();
         assertThat(captor.getValue().carrierText).isEqualTo(TEST_CARRIER);
     }
 
     @Test
+    public void carrierText_hasSatelliteText_isSatelliteTrue_textUsed() {
+        reset(mCarrierTextCallback);
+        List<SubscriptionInfo> list = new ArrayList<>();
+        list.add(TEST_SUBSCRIPTION);
+        when(mKeyguardUpdateMonitor.getSimState(anyInt())).thenReturn(
+                TelephonyManager.SIM_STATE_READY);
+        when(mKeyguardUpdateMonitor.getFilteredSubscriptionInfo()).thenReturn(list);
+        mKeyguardUpdateMonitor.mServiceStates = new HashMap<>();
+
+        // WHEN the satellite text is non-null
+        mSatelliteViewModel.getCarrierText().setValue("Satellite Test Text");
+        mTestScope.getTestScheduler().runCurrent();
+
+        ArgumentCaptor<CarrierTextManager.CarrierTextCallbackInfo> captor =
+                ArgumentCaptor.forClass(
+                        CarrierTextManager.CarrierTextCallbackInfo.class);
+        FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
+
+        // THEN satellite mode is true and the satellite text is used
+        verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
+        assertThat(captor.getValue().isInSatelliteMode).isTrue();
+        assertThat(captor.getValue().carrierText).isEqualTo("Satellite Test Text");
+    }
+
+    @Test
     public void carrierText_satelliteTextUpdates_autoTriggersCallback() {
         reset(mCarrierTextCallback);
         List<SubscriptionInfo> list = new ArrayList<>();
@@ -517,6 +546,7 @@
         FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
         verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
         // AND use the satellite text as the carrier text
+        assertThat(captor.getValue().isInSatelliteMode).isTrue();
         assertThat(captor.getValue().carrierText).isEqualTo("Test satellite text");
 
         // WHEN the satellite text is reset to null
@@ -528,6 +558,7 @@
         // that doesn't include the satellite info
         FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
         verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
+        assertThat(captor.getValue().isInSatelliteMode).isFalse();
         assertThat(captor.getValue().carrierText).isEqualTo(TEST_CARRIER);
     }
 
@@ -566,10 +597,11 @@
         mCarrierTextManager.setListening(mCarrierTextCallback);
 
         // THEN we should automatically re-trigger #updateCarrierText and get callback info
-        // that includes the new satellite text
+        // that includes the new satellite state and text
         mTestScope.getTestScheduler().runCurrent();
         FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
         verify(mCarrierTextCallback).updateCarrierInfo(captor.capture());
+        assertThat(captor.getValue().isInSatelliteMode).isTrue();
         assertThat(captor.getValue().carrierText).isEqualTo("New satellite text");
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 99b5a4b..cfa74f0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -181,7 +181,7 @@
                 ArgumentCaptor.forClass(ContentObserver.class);
         mController.init();
         mExecutor.runAllReady();
-        verify(mSecureSettings).registerContentObserverForUser(
+        verify(mSecureSettings).registerContentObserverForUserSync(
                 eq(Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK),
                     anyBoolean(), observerCaptor.capture(), eq(UserHandle.USER_ALL));
         ContentObserver observer = observerCaptor.getValue();
@@ -247,7 +247,7 @@
                 ArgumentCaptor.forClass(ContentObserver.class);
         mController.init();
         mExecutor.runAllReady();
-        verify(mSecureSettings).registerContentObserverForUser(
+        verify(mSecureSettings).registerContentObserverForUserSync(
                 eq(Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED), anyBoolean(),
                     observerCaptor.capture(), eq(UserHandle.USER_ALL));
         ContentObserver observer = observerCaptor.getValue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
index 92b06ba..138fed2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
@@ -393,7 +393,7 @@
 
         mWindowMagnificationSettings.showSettingPanel();
 
-        verify(mSecureSettings).registerContentObserverForUser(
+        verify(mSecureSettings).registerContentObserverForUserSync(
                 eq(ACCESSIBILITY_MAGNIFICATION_CAPABILITY),
                 any(ContentObserver.class),
                 eq(UserHandle.USER_CURRENT));
@@ -408,7 +408,7 @@
         mWindowMagnificationSettings.showSettingPanel();
         mWindowMagnificationSettings.hideSettingPanel();
 
-        verify(mSecureSettings).unregisterContentObserver(any(ContentObserver.class));
+        verify(mSecureSettings).unregisterContentObserverSync(any(ContentObserver.class));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt
index bd446b9..1d1329ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/data/repository/AccessibilityQsShortcutsRepositoryImplForDeviceTest.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.qs.tiles.ColorCorrectionTile
 import com.android.systemui.qs.tiles.ColorInversionTile
 import com.android.systemui.qs.tiles.FontScalingTile
+import com.android.systemui.qs.tiles.HearingDevicesTile
 import com.android.systemui.qs.tiles.OneHandedModeTile
 import com.android.systemui.qs.tiles.ReduceBrightColorsTile
 import com.android.systemui.util.mockito.whenever
@@ -94,7 +95,7 @@
     fun testTileSpecToComponentMappingContent() {
         val mapping = AccessibilityQsShortcutsRepositoryImpl.TILE_SPEC_TO_COMPONENT_MAPPING
 
-        assertThat(mapping.size).isEqualTo(5)
+        assertThat(mapping.size).isEqualTo(6)
         assertThat(mapping[ColorCorrectionTile.TILE_SPEC])
             .isEqualTo(AccessibilityShortcutController.DALTONIZER_TILE_COMPONENT_NAME)
         assertThat(mapping[ColorInversionTile.TILE_SPEC])
@@ -107,6 +108,10 @@
             )
         assertThat(mapping[FontScalingTile.TILE_SPEC])
             .isEqualTo(AccessibilityShortcutController.FONT_SIZE_TILE_COMPONENT_NAME)
+        assertThat(mapping[HearingDevicesTile.TILE_SPEC])
+            .isEqualTo(
+                AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_TILE_COMPONENT_NAME
+            )
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
index 8895a5e..0db0de2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.accessibility.hearingaid;
 
+import static com.android.systemui.accessibility.hearingaid.HearingDevicesDialogDelegate.LIVE_CAPTION_INTENT;
 import static com.android.systemui.statusbar.phone.SystemUIDialog.DEFAULT_DISMISS_ON_DEVICE_LOCK;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -24,16 +25,24 @@
 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.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.ComponentName;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
 import android.media.AudioManager;
 import android.os.Handler;
+import android.platform.test.annotations.EnableFlags;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.View;
+import android.widget.LinearLayout;
 
 import androidx.test.filters.SmallTest;
 
@@ -44,6 +53,7 @@
 import com.android.settingslib.bluetooth.LocalBluetoothAdapter;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.DialogTransitionAnimator;
 import com.android.systemui.bluetooth.qsdialog.DeviceItem;
@@ -54,6 +64,7 @@
 import com.android.systemui.statusbar.phone.SystemUIDialog;
 import com.android.systemui.statusbar.phone.SystemUIDialogManager;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -75,6 +86,10 @@
     public MockitoRule mockito = MockitoJUnit.rule();
 
     private static final String DEVICE_ADDRESS = "AA:BB:CC:DD:EE:FF";
+    private static final String TEST_PKG = "pkg";
+    private static final String TEST_CLS = "cls";
+    private static final ComponentName TEST_COMPONENT = new ComponentName(TEST_PKG, TEST_CLS);
+    private static final String TEST_LABEL = "label";
 
     @Mock
     private SystemUIDialog.Factory mSystemUIDialogFactory;
@@ -104,6 +119,12 @@
     private CachedBluetoothDevice mCachedDevice;
     @Mock
     private DeviceItem mHearingDeviceItem;
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private ActivityInfo mActivityInfo;
+    @Mock
+    private Drawable mDrawable;
     private SystemUIDialog mDialog;
     private HearingDevicesDialogDelegate mDialogDelegate;
     private TestableLooper mTestableLooper;
@@ -122,6 +143,7 @@
         when(mSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mSysUiState);
         when(mCachedDevice.getAddress()).thenReturn(DEVICE_ADDRESS);
         when(mHearingDeviceItem.getCachedBluetoothDevice()).thenReturn(mCachedDevice);
+        mContext.setMockPackageManager(mPackageManager);
 
         setUpPairNewDeviceDialog();
 
@@ -170,6 +192,45 @@
         verify(mCachedDevice).disconnect();
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_HEARING_DEVICES_DIALOG_RELATED_TOOLS)
+    public void showDialog_hasLiveCaption_noRelatedToolsInConfig_showOneRelatedTool() {
+        when(mPackageManager.queryIntentActivities(
+                eq(LIVE_CAPTION_INTENT), anyInt())).thenReturn(
+                List.of(new ResolveInfo()));
+        mContext.getOrCreateTestableResources().addOverride(
+                R.array.config_quickSettingsHearingDevicesRelatedToolName, new String[]{});
+
+        setUpPairNewDeviceDialog();
+        mDialog.show();
+
+        LinearLayout relatedToolsView = (LinearLayout) getRelatedToolsView(mDialog);
+        assertThat(relatedToolsView.getChildCount()).isEqualTo(1);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_HEARING_DEVICES_DIALOG_RELATED_TOOLS)
+    public void showDialog_hasLiveCaption_oneRelatedToolInConfig_showTwoRelatedTools()
+            throws PackageManager.NameNotFoundException {
+        when(mPackageManager.queryIntentActivities(
+                eq(LIVE_CAPTION_INTENT), anyInt())).thenReturn(
+                List.of(new ResolveInfo()));
+        mContext.getOrCreateTestableResources().addOverride(
+                R.array.config_quickSettingsHearingDevicesRelatedToolName,
+                new String[]{TEST_PKG + "/" + TEST_CLS});
+        when(mPackageManager.getActivityInfo(eq(TEST_COMPONENT), anyInt())).thenReturn(
+                mActivityInfo);
+        when(mActivityInfo.loadLabel(mPackageManager)).thenReturn(TEST_LABEL);
+        when(mActivityInfo.loadIcon(mPackageManager)).thenReturn(mDrawable);
+        when(mActivityInfo.getComponentName()).thenReturn(TEST_COMPONENT);
+
+        setUpPairNewDeviceDialog();
+        mDialog.show();
+
+        LinearLayout relatedToolsView = (LinearLayout) getRelatedToolsView(mDialog);
+        assertThat(relatedToolsView.getChildCount()).isEqualTo(2);
+    }
+
     private void setUpPairNewDeviceDialog() {
         mDialogDelegate = new HearingDevicesDialogDelegate(
                 mContext,
@@ -219,4 +280,18 @@
     private View getPairNewDeviceButton(SystemUIDialog dialog) {
         return dialog.requireViewById(R.id.pair_new_device_button);
     }
+
+    private View getRelatedToolsView(SystemUIDialog dialog) {
+        return dialog.requireViewById(R.id.related_tools_container);
+    }
+
+    @After
+    public void reset() {
+        if (mDialogDelegate != null) {
+            mDialogDelegate = null;
+        }
+        if (mDialog != null) {
+            mDialog.dismiss();
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java
new file mode 100644
index 0000000..7172923
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesToolItemParserTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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 static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.when;
+
+import static java.util.Collections.emptyList;
+
+import android.content.ComponentName;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+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;
+
+import java.util.List;
+
+/**
+ * Tests for {@link HearingDevicesToolItemParser}.
+ */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class HearingDevicesToolItemParserTest extends SysuiTestCase {
+    @Rule
+    public MockitoRule mockito = MockitoJUnit.rule();
+
+    @Mock
+    private PackageManager mPackageManager;
+    @Mock
+    private ActivityInfo mActivityInfo;
+    @Mock
+    private Drawable mDrawable;
+    private static final String TEST_PKG = "pkg";
+    private static final String TEST_CLS = "cls";
+    private static final ComponentName TEST_COMPONENT = new ComponentName(TEST_PKG, TEST_CLS);
+    private static final String TEST_NO_EXIST_PKG = "NoPkg";
+    private static final String TEST_NO_EXIST_CLS = "NoCls";
+    private static final ComponentName TEST_NO_EXIST_COMPONENT = new ComponentName(
+            TEST_NO_EXIST_PKG, TEST_NO_EXIST_CLS);
+
+    private static final String TEST_LABEL = "label";
+
+    @Before
+    public void setUp() throws PackageManager.NameNotFoundException {
+        mContext.setMockPackageManager(mPackageManager);
+
+        when(mPackageManager.getActivityInfo(eq(TEST_COMPONENT), anyInt())).thenReturn(
+                mActivityInfo);
+        when(mPackageManager.getActivityInfo(eq(TEST_NO_EXIST_COMPONENT), anyInt())).thenThrow(
+                new PackageManager.NameNotFoundException());
+        when(mActivityInfo.loadLabel(mPackageManager)).thenReturn(TEST_LABEL);
+        when(mActivityInfo.loadIcon(mPackageManager)).thenReturn(mDrawable);
+        when(mActivityInfo.getComponentName()).thenReturn(TEST_COMPONENT);
+    }
+
+    @Test
+    public void parseStringArray_noString_emptyResult() {
+        assertThat(HearingDevicesToolItemParser.parseStringArray(mContext, new String[]{},
+                new String[]{})).isEqualTo(emptyList());
+    }
+
+    @Test
+    public void parseStringArray_oneToolName_oneExpectedToolItem() {
+        String[] toolName = new String[]{TEST_PKG + "/" + TEST_CLS};
+
+        List<ToolItem> toolItemList = HearingDevicesToolItemParser.parseStringArray(mContext,
+                toolName, new String[]{});
+
+        assertThat(toolItemList.size()).isEqualTo(1);
+        assertThat(toolItemList.get(0).getToolName()).isEqualTo(TEST_LABEL);
+        assertThat(toolItemList.get(0).getToolIntent().getComponent()).isEqualTo(TEST_COMPONENT);
+    }
+
+    @Test
+    public void parseStringArray_fourToolName_maxThreeToolItem() {
+        String componentNameString = TEST_PKG + "/" + TEST_CLS;
+        String[] fourToolName =
+                new String[]{componentNameString, componentNameString, componentNameString,
+                        componentNameString};
+
+        List<ToolItem> toolItemList = HearingDevicesToolItemParser.parseStringArray(mContext,
+                fourToolName, new String[]{});
+        assertThat(toolItemList.size()).isEqualTo(HearingDevicesToolItemParser.MAX_NUM);
+    }
+
+    @Test
+    public void parseStringArray_oneWrongFormatToolName_noToolItem() {
+        String[] wrongFormatToolName = new String[]{TEST_PKG};
+
+        List<ToolItem> toolItemList = HearingDevicesToolItemParser.parseStringArray(mContext,
+                wrongFormatToolName, new String[]{});
+        assertThat(toolItemList.size()).isEqualTo(0);
+    }
+
+    @Test
+    public void parseStringArray_oneNotExistToolName_noToolItem() {
+        String[] notExistToolName = new String[]{TEST_NO_EXIST_PKG + "/" + TEST_NO_EXIST_CLS};
+
+        List<ToolItem> toolItemList = HearingDevicesToolItemParser.parseStringArray(mContext,
+                notExistToolName, new String[]{});
+        assertThat(toolItemList.size()).isEqualTo(0);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
index d01d57e..358e8cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ambient/touch/TouchMonitorTest.java
@@ -22,6 +22,8 @@
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
@@ -45,6 +47,7 @@
 
 import androidx.annotation.NonNull;
 import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleObserver;
 import androidx.lifecycle.LifecycleOwner;
 import androidx.lifecycle.LifecycleRegistry;
 import androidx.test.filters.SmallTest;
@@ -67,6 +70,7 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
@@ -119,6 +123,8 @@
 
         private final KosmosJavaAdapter mKosmos;
 
+        private ArrayList<LifecycleObserver> mLifecycleObservers = new ArrayList<>();
+
 
         Environment(Set<TouchHandler> handlers, KosmosJavaAdapter kosmos) {
             mLifecycleOwner = new SimpleLifecycleOwner();
@@ -147,8 +153,14 @@
             mMonitor = new TouchMonitor(mExecutor, mBackgroundExecutor, mLifecycleRegistry,
                     mInputFactory, mDisplayHelper, mKosmos.getConfigurationInteractor(),
                     handlers, mIWindowManager,  0);
+            clearInvocations(mLifecycleRegistry);
             mMonitor.init();
 
+            ArgumentCaptor<LifecycleObserver> observerCaptor =
+                    ArgumentCaptor.forClass(LifecycleObserver.class);
+            verify(mLifecycleRegistry, atLeast(1)).addObserver(observerCaptor.capture());
+            mLifecycleObservers.addAll(observerCaptor.getAllValues());
+
             updateLifecycle(Lifecycle.State.RESUMED);
 
             // Capture creation request.
@@ -187,6 +199,16 @@
             verify(mInputSession).dispose();
             Mockito.clearInvocations(mInputSession);
         }
+
+        void destroyMonitor() {
+            mMonitor.destroy();
+        }
+
+        void verifyLifecycleObserversUnregistered() {
+            for (LifecycleObserver observer : mLifecycleObservers) {
+                verify(mLifecycleRegistry).removeObserver(observer);
+            }
+        }
     }
 
     @Test
@@ -642,6 +664,16 @@
         verify(callback).onRemoved();
     }
 
+    @Test
+    public void testDestroy_cleansUpLifecycleObserver() {
+        final TouchHandler touchHandler = createTouchHandler();
+
+        final Environment environment = new Environment(Stream.of(touchHandler)
+                .collect(Collectors.toCollection(HashSet::new)), mKosmos);
+        environment.destroyMonitor();
+        environment.verifyLifecycleObserversUnregistered();
+    }
+
     private GestureDetector.OnGestureListener registerGestureListener(TouchHandler handler) {
         final GestureDetector.OnGestureListener gestureListener = Mockito.mock(
                 GestureDetector.OnGestureListener.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index e81369d..9a99ed7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -18,6 +18,7 @@
 import android.app.ActivityTaskManager
 import android.app.admin.DevicePolicyManager
 import android.content.pm.PackageManager
+import android.content.res.Configuration
 import android.hardware.biometrics.BiometricAuthenticator
 import android.hardware.biometrics.BiometricConstants
 import android.hardware.biometrics.BiometricManager
@@ -127,6 +128,12 @@
     private lateinit var packageManager: PackageManager
     @Mock private lateinit var activityTaskManager: ActivityTaskManager
 
+    private lateinit var displayRepository: FakeDisplayRepository
+    private lateinit var displayStateInteractor: DisplayStateInteractor
+    private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
+    private lateinit var biometricStatusInteractor: BiometricStatusInteractor
+    private lateinit var iconProvider: IconProvider
+
     private val testScope = TestScope(StandardTestDispatcher())
     private val fakeExecutor = FakeExecutor(FakeSystemClock())
     private val biometricPromptRepository = FakePromptRepository()
@@ -142,17 +149,12 @@
     private val promptSelectorInteractor by lazy {
         PromptSelectorInteractorImpl(
             fingerprintRepository,
+            displayStateInteractor,
             biometricPromptRepository,
             lockPatternUtils,
         )
     }
 
-    private lateinit var displayRepository: FakeDisplayRepository
-    private lateinit var displayStateInteractor: DisplayStateInteractor
-    private lateinit var udfpsOverlayInteractor: UdfpsOverlayInteractor
-    private lateinit var biometricStatusInteractor: BiometricStatusInteractor
-    private lateinit var iconProvider: IconProvider
-
     private val credentialViewModel = CredentialViewModel(mContext, bpCredentialInteractor)
     private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android)
 
@@ -392,6 +394,33 @@
     }
 
     @Test
+    fun testAnimateToCredentialUI_rotateCredentialUI() {
+        val container = initializeFingerprintContainer(
+            authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK or
+                    BiometricManager.Authenticators.DEVICE_CREDENTIAL
+        )
+        container.animateToCredentialUI(false)
+        waitForIdleSync()
+
+        assertThat(container.hasCredentialView()).isTrue()
+        assertThat(container.hasBiometricPrompt()).isFalse()
+
+        // Check credential view persists after new attachment
+        container.onAttachedToWindow()
+
+        assertThat(container.hasCredentialView()).isTrue()
+        assertThat(container.hasBiometricPrompt()).isFalse()
+
+        val configuration = Configuration(context.resources.configuration)
+        configuration.orientation = Configuration.ORIENTATION_LANDSCAPE
+        container.dispatchConfigurationChanged(configuration)
+        waitForIdleSync()
+
+        assertThat(container.hasCredentialView()).isTrue()
+        assertThat(container.hasBiometricPrompt()).isFalse()
+    }
+
+    @Test
     fun testShowBiometricUI() {
         mSetFlagsRule.disableFlags(FLAG_CONSTRAINT_BP)
         val container = initializeFingerprintContainer()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt
index 9ba56d2..6391986 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt
@@ -82,7 +82,12 @@
             val keys =
                 captureMany<String> {
                     verify(secureSettings)
-                        .registerContentObserverForUser(capture(), anyBoolean(), any(), eq(USER_ID))
+                        .registerContentObserverForUserSync(
+                            capture(),
+                            anyBoolean(),
+                            any(),
+                            eq(USER_ID)
+                        )
                 }
 
             assertThat(keys).containsExactly(FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION)
@@ -102,7 +107,7 @@
             val observer =
                 withArgCaptor<ContentObserver> {
                     verify(secureSettings)
-                        .registerContentObserverForUser(
+                        .registerContentObserverForUserSync(
                             eq(setting),
                             anyBoolean(),
                             capture(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
index 3102a84..6e78e33 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorImplTest.kt
@@ -25,13 +25,16 @@
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.data.repository.FakeDisplayStateRepository
 import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
 import com.android.systemui.biometrics.data.repository.FakePromptRepository
 import com.android.systemui.biometrics.faceSensorPropertiesInternal
 import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal
 import com.android.systemui.biometrics.shared.model.BiometricModalities
+import com.android.systemui.biometrics.shared.model.DisplayRotation
 import com.android.systemui.biometrics.shared.model.PromptKind
 import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.display.data.repository.FakeDisplayRepository
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.whenever
@@ -75,12 +78,30 @@
     private val promptRepository = FakePromptRepository()
     private val fakeExecutor = FakeExecutor(FakeSystemClock())
 
+    private lateinit var displayStateRepository: FakeDisplayStateRepository
+    private lateinit var displayRepository: FakeDisplayRepository
+    private lateinit var displayStateInteractor: DisplayStateInteractor
     private lateinit var interactor: PromptSelectorInteractor
 
     @Before
     fun setup() {
+        displayStateRepository = FakeDisplayStateRepository()
+        displayRepository = FakeDisplayRepository()
+        displayStateInteractor =
+            DisplayStateInteractorImpl(
+                testScope.backgroundScope,
+                mContext,
+                fakeExecutor,
+                displayStateRepository,
+                displayRepository,
+            )
         interactor =
-            PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils)
+            PromptSelectorInteractorImpl(
+                fingerprintRepository,
+                displayStateInteractor,
+                promptRepository,
+                lockPatternUtils
+            )
     }
 
     private fun basicPromptInfo() =
@@ -155,7 +176,8 @@
             modalities,
             CHALLENGE,
             OP_PACKAGE_NAME,
-            false /*onSwitchToCredential*/
+            onSwitchToCredential = false,
+            isLandscape = false,
         )
 
         assertThat(currentPrompt).isNotNull()
@@ -200,22 +222,49 @@
     fun promptKind_isBiometric_whenBiometricAllowed() =
         testScope.runTest {
             setUserCredentialType(isPassword = true)
-            val info = basicPromptInfo()
 
             val promptKind by collectLastValue(interactor.promptKind)
             assertThat(promptKind).isEqualTo(PromptKind.None)
 
-            interactor.setPrompt(
-                info,
-                USER_ID,
-                REQUEST_ID,
-                modalities,
-                CHALLENGE,
-                OP_PACKAGE_NAME,
-                false /*onSwitchToCredential*/
-            )
+            setPrompt()
 
-            assertThat(promptKind?.isBiometric()).isTrue()
+            assertThat(promptKind?.isOnePanePortraitBiometric()).isTrue()
+
+            interactor.resetPrompt(REQUEST_ID)
+            verifyUnset()
+        }
+
+    @Test
+    fun promptKind_isBiometricTwoPane_whenBiometricAllowed_landscape() =
+        testScope.runTest {
+            setUserCredentialType(isPassword = true)
+            displayStateRepository.setIsLargeScreen(false)
+            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+
+            val promptKind by collectLastValue(interactor.promptKind)
+            assertThat(promptKind).isEqualTo(PromptKind.None)
+
+            setPrompt()
+
+            assertThat(promptKind?.isTwoPaneLandscapeBiometric()).isTrue()
+
+            interactor.resetPrompt(REQUEST_ID)
+            verifyUnset()
+        }
+
+    @Test
+    fun promptKind_isBiometricOnePane_whenBiometricAllowed_largeScreenLandscape() =
+        testScope.runTest {
+            setUserCredentialType(isPassword = true)
+            displayStateRepository.setIsLargeScreen(true)
+            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
+
+            val promptKind by collectLastValue(interactor.promptKind)
+            assertThat(promptKind).isEqualTo(PromptKind.None)
+
+            setPrompt()
+
+            assertThat(promptKind?.isOnePaneLargeScreenLandscapeBiometric()).isTrue()
 
             interactor.resetPrompt(REQUEST_ID)
             verifyUnset()
@@ -225,20 +274,11 @@
     fun promptKind_isCredential_onSwitchToCredential() =
         testScope.runTest {
             setUserCredentialType(isPassword = true)
-            val info = basicPromptInfo()
 
             val promptKind by collectLastValue(interactor.promptKind)
             assertThat(promptKind).isEqualTo(PromptKind.None)
 
-            interactor.setPrompt(
-                info,
-                USER_ID,
-                REQUEST_ID,
-                modalities,
-                CHALLENGE,
-                OP_PACKAGE_NAME,
-                true /*onSwitchToCredential*/
-            )
+            setPrompt(onSwitchToCredential = true)
 
             assertThat(promptKind).isEqualTo(PromptKind.Password)
 
@@ -259,15 +299,7 @@
             val promptKind by collectLastValue(interactor.promptKind)
             assertThat(promptKind).isEqualTo(PromptKind.None)
 
-            interactor.setPrompt(
-                info,
-                USER_ID,
-                REQUEST_ID,
-                modalities,
-                CHALLENGE,
-                OP_PACKAGE_NAME,
-                false /*onSwitchToCredential*/
-            )
+            setPrompt(info)
 
             assertThat(promptKind).isEqualTo(PromptKind.Password)
 
@@ -292,15 +324,7 @@
             val promptKind by collectLastValue(interactor.promptKind)
             assertThat(promptKind).isEqualTo(PromptKind.None)
 
-            interactor.setPrompt(
-                info,
-                USER_ID,
-                REQUEST_ID,
-                modalities,
-                CHALLENGE,
-                OP_PACKAGE_NAME,
-                false /*onSwitchToCredential*/
-            )
+            setPrompt(info)
 
             assertThat(promptKind).isEqualTo(PromptKind.Password)
 
@@ -312,6 +336,7 @@
     fun promptKind_isBiometric_whenBiometricIsNotAllowed_withVerticalList() =
         testScope.runTest {
             setUserCredentialType(isPassword = true)
+            displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90)
             val info =
                 basicPromptInfo().apply {
                     isDeviceCredentialAllowed = true
@@ -322,22 +347,32 @@
             val promptKind by collectLastValue(interactor.promptKind)
             assertThat(promptKind).isEqualTo(PromptKind.None)
 
-            interactor.setPrompt(
-                info,
-                USER_ID,
-                REQUEST_ID,
-                modalities,
-                CHALLENGE,
-                OP_PACKAGE_NAME,
-                false /*onSwitchToCredential*/
-            )
+            setPrompt(info)
 
-            assertThat(promptKind?.isBiometric()).isTrue()
+            assertThat(promptKind?.isOnePaneNoSensorLandscapeBiometric()).isTrue()
 
             interactor.resetPrompt(REQUEST_ID)
             verifyUnset()
         }
 
+    private fun setPrompt(
+        info: PromptInfo = basicPromptInfo(),
+        onSwitchToCredential: Boolean = false
+    ) {
+        interactor.setPrompt(
+            info,
+            USER_ID,
+            REQUEST_ID,
+            modalities,
+            CHALLENGE,
+            OP_PACKAGE_NAME,
+            onSwitchToCredential = onSwitchToCredential,
+            isLandscape =
+                displayStateRepository.currentRotation.value == DisplayRotation.ROTATION_90 ||
+                    displayStateRepository.currentRotation.value == DisplayRotation.ROTATION_270,
+        )
+    }
+
     private fun TestScope.useCredentialAndReset(kind: PromptKind) {
         setUserCredentialType(
             isPin = kind == PromptKind.Pin,
@@ -366,7 +401,8 @@
             BiometricModalities(),
             CHALLENGE,
             OP_PACKAGE_NAME,
-            false /*onSwitchToCredential*/
+            onSwitchToCredential = false,
+            isLandscape = false,
         )
 
         // not using biometrics, should be null with no fallback option
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 c177511..f46cfdc 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
@@ -383,11 +383,25 @@
             }
 
             if (testCase.isFaceOnly) {
-                val expectedIconAsset = R.raw.face_dialog_authenticating
+                val shouldRepeatAnimation by collectLastValue(iconViewModel.shouldRepeatAnimation)
+                val shouldPulseAnimation by collectLastValue(iconViewModel.shouldPulseAnimation)
+                val lastPulseLightToDark by collectLastValue(iconViewModel.lastPulseLightToDark)
+
+                val expectedIconAsset =
+                    if (shouldPulseAnimation!!) {
+                        if (lastPulseLightToDark!!) {
+                            R.drawable.face_dialog_pulse_dark_to_light
+                        } else {
+                            R.drawable.face_dialog_pulse_light_to_dark
+                        }
+                    } else {
+                        R.drawable.face_dialog_pulse_dark_to_light
+                    }
                 assertThat(iconAsset).isEqualTo(expectedIconAsset)
                 assertThat(iconContentDescriptionId)
                     .isEqualTo(R.string.biometric_dialog_face_icon_description_authenticating)
                 assertThat(shouldAnimateIconView).isEqualTo(true)
+                assertThat(shouldRepeatAnimation).isEqualTo(true)
             }
 
             if (testCase.isCoex) {
@@ -409,11 +423,26 @@
                     }
                 } else {
                     // implicit flow
-                    val expectedIconAsset = R.raw.face_dialog_authenticating
+                    val shouldRepeatAnimation by
+                        collectLastValue(iconViewModel.shouldRepeatAnimation)
+                    val shouldPulseAnimation by collectLastValue(iconViewModel.shouldPulseAnimation)
+                    val lastPulseLightToDark by collectLastValue(iconViewModel.lastPulseLightToDark)
+
+                    val expectedIconAsset =
+                        if (shouldPulseAnimation!!) {
+                            if (lastPulseLightToDark!!) {
+                                R.drawable.face_dialog_pulse_dark_to_light
+                            } else {
+                                R.drawable.face_dialog_pulse_light_to_dark
+                            }
+                        } else {
+                            R.drawable.face_dialog_pulse_dark_to_light
+                        }
                     assertThat(iconAsset).isEqualTo(expectedIconAsset)
                     assertThat(iconContentDescriptionId)
                         .isEqualTo(R.string.biometric_dialog_face_icon_description_authenticating)
                     assertThat(shouldAnimateIconView).isEqualTo(true)
+                    assertThat(shouldRepeatAnimation).isEqualTo(true)
                 }
             }
         }
@@ -503,9 +532,14 @@
         }
 
         if (testCase.isFaceOnly) {
+            val shouldRepeatAnimation by collectLastValue(iconViewModel.shouldRepeatAnimation)
+            val shouldPulseAnimation by collectLastValue(iconViewModel.shouldPulseAnimation)
+
+            assertThat(shouldPulseAnimation!!).isEqualTo(false)
             assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_dark_to_error)
             assertThat(iconContentDescriptionId).isEqualTo(R.string.keyguard_face_failed)
             assertThat(shouldAnimateIconView).isEqualTo(true)
+            assertThat(shouldRepeatAnimation).isEqualTo(false)
 
             // Clear error, go to idle
             errorJob.join()
@@ -514,6 +548,7 @@
             assertThat(iconContentDescriptionId)
                 .isEqualTo(R.string.biometric_dialog_face_icon_description_idle)
             assertThat(shouldAnimateIconView).isEqualTo(true)
+            assertThat(shouldRepeatAnimation).isEqualTo(false)
         }
 
         if (testCase.isCoex) {
@@ -596,10 +631,15 @@
 
             // If co-ex, using implicit flow (explicit flow always requires confirmation)
             if (testCase.isFaceOnly || testCase.isCoex) {
+                val shouldRepeatAnimation by collectLastValue(iconViewModel.shouldRepeatAnimation)
+                val shouldPulseAnimation by collectLastValue(iconViewModel.shouldPulseAnimation)
+
+                assertThat(shouldPulseAnimation!!).isEqualTo(false)
                 assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_dark_to_checkmark)
                 assertThat(iconContentDescriptionId)
                     .isEqualTo(R.string.biometric_dialog_face_icon_description_authenticated)
                 assertThat(shouldAnimateIconView).isEqualTo(true)
+                assertThat(shouldRepeatAnimation).isEqualTo(false)
             }
         }
     }
@@ -621,10 +661,15 @@
             )
 
             if (testCase.isFaceOnly) {
+                val shouldRepeatAnimation by collectLastValue(iconViewModel.shouldRepeatAnimation)
+                val shouldPulseAnimation by collectLastValue(iconViewModel.shouldPulseAnimation)
+
+                assertThat(shouldPulseAnimation!!).isEqualTo(false)
                 assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_wink_from_dark)
                 assertThat(iconContentDescriptionId)
                     .isEqualTo(R.string.biometric_dialog_face_icon_description_authenticated)
                 assertThat(shouldAnimateIconView).isEqualTo(true)
+                assertThat(shouldRepeatAnimation).isEqualTo(false)
             }
 
             // explicit flow because confirmation requested
@@ -666,10 +711,15 @@
             viewModel.confirmAuthenticated()
 
             if (testCase.isFaceOnly) {
+                val shouldRepeatAnimation by collectLastValue(iconViewModel.shouldRepeatAnimation)
+                val shouldPulseAnimation by collectLastValue(iconViewModel.shouldPulseAnimation)
+
+                assertThat(shouldPulseAnimation!!).isEqualTo(false)
                 assertThat(iconAsset).isEqualTo(R.drawable.face_dialog_dark_to_checkmark)
                 assertThat(iconContentDescriptionId)
                     .isEqualTo(R.string.biometric_dialog_face_icon_description_confirmed)
                 assertThat(shouldAnimateIconView).isEqualTo(true)
+                assertThat(shouldRepeatAnimation).isEqualTo(false)
             }
 
             // explicit flow because confirmation requested
@@ -1407,7 +1457,12 @@
         runningTaskInfo.topActivity = topActivity
         whenever(activityTaskManager.getTasks(1)).thenReturn(listOf(runningTaskInfo))
         selector =
-            PromptSelectorInteractorImpl(fingerprintRepository, promptRepository, lockPatternUtils)
+            PromptSelectorInteractorImpl(
+                fingerprintRepository,
+                displayStateInteractor,
+                promptRepository,
+                lockPatternUtils
+            )
         selector.resetPrompt(REQUEST_ID)
 
         viewModel =
@@ -1643,7 +1698,8 @@
         BiometricModalities(fingerprintProperties = fingerprint, faceProperties = face),
         CHALLENGE,
         packageName,
-        false /*onUseDeviceCredential*/
+        onSwitchToCredential = false,
+        isLandscape = false,
     )
 }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
index 07c980b..18bd960b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/complication/DreamHomeControlsComplicationTest.java
@@ -132,7 +132,7 @@
     public void complicationAvailability_serviceNotAvailable_noFavorites_doNotAddComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent, mMonitor);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor, false);
         registrant.start();
 
         setHaveFavorites(false);
@@ -145,7 +145,7 @@
     public void complicationAvailability_serviceAvailable_noFavorites_doNotAddComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent, mMonitor);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor, false);
         registrant.start();
 
         setHaveFavorites(false);
@@ -158,7 +158,7 @@
     public void complicationAvailability_serviceAvailable_noFavorites_panel_addComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent, mMonitor);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor, false);
         registrant.start();
 
         setHaveFavorites(false);
@@ -171,7 +171,7 @@
     public void complicationAvailability_serviceNotAvailable_haveFavorites_doNotAddComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent, mMonitor);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor, false);
         registrant.start();
 
         setHaveFavorites(true);
@@ -184,7 +184,7 @@
     public void complicationAvailability_serviceAvailable_haveFavorites_addComplication() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent, mMonitor);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor, false);
         registrant.start();
 
         setHaveFavorites(true);
@@ -197,7 +197,7 @@
     public void complicationAvailability_checkAvailabilityWhenDreamOverlayBecomesActive() {
         final DreamHomeControlsComplication.Registrant registrant =
                 new DreamHomeControlsComplication.Registrant(mComplication,
-                        mDreamOverlayStateController, mControlsComponent, mMonitor);
+                        mDreamOverlayStateController, mControlsComponent, mMonitor, false);
         registrant.start();
 
         setServiceAvailable(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt
index 590989d..154c373 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt
@@ -332,7 +332,7 @@
     }
 
     private fun attachRepositoryToSettings() {
-        secureSettings.registerContentObserver(
+        secureSettings.registerContentObserverSync(
             SETTING_SHOW,
             object : ContentObserver(null) {
                 override fun onChange(selfChange: Boolean) {
@@ -343,7 +343,7 @@
             }
         )
 
-        secureSettings.registerContentObserver(
+        secureSettings.registerContentObserverSync(
             SETTING_ACTION,
             object : ContentObserver(null) {
                 override fun onChange(selfChange: Boolean) {
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 cfe37ee..e2cca38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogLiteTest.java
@@ -42,7 +42,6 @@
 import android.os.UserManager;
 import android.provider.Settings;
 import android.service.dreams.IDreamManager;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.GestureDetector;
 import android.view.IWindowManager;
@@ -52,6 +51,7 @@
 import android.window.OnBackInvokedCallback;
 import android.window.OnBackInvokedDispatcher;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
@@ -99,7 +99,7 @@
 import java.util.concurrent.Executor;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class GlobalActionsDialogLiteTest extends SysuiTestCase {
     private GlobalActionsDialogLite mGlobalActionsDialogLite;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java
index 84b1c00..87dd9b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsGridLayoutTest.java
@@ -21,11 +21,11 @@
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.spy;
 
-import android.testing.AndroidTestingRunner;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -40,7 +40,7 @@
  * Tests for {@link ListGridLayout}.
  */
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class GlobalActionsGridLayoutTest extends SysuiTestCase {
 
     private GlobalActionsGridLayout mGridLayout;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java
index 16dcd65..ea0f4d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsLayoutTest.java
@@ -24,11 +24,11 @@
 import static org.mockito.Mockito.spy;
 
 import android.content.Context;
-import android.testing.AndroidTestingRunner;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.MultiListLayout;
@@ -44,7 +44,7 @@
  * Tests for {@link ListGridLayout}.
  */
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class GlobalActionsLayoutTest extends SysuiTestCase {
 
     private TestLayout mLayout;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/ListGridLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/ListGridLayoutTest.java
index 4c65b90..a10457f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/ListGridLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/ListGridLayoutTest.java
@@ -18,11 +18,11 @@
 
 import static junit.framework.Assert.assertEquals;
 
-import android.testing.AndroidTestingRunner;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -36,7 +36,7 @@
  * Tests for {@link ListGridLayout}.
  */
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class ListGridLayoutTest extends SysuiTestCase {
 
     private ListGridLayout mListGridLayout;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/ShutdownUiTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/ShutdownUiTest.java
index 28c01ad..73509e2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/ShutdownUiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/ShutdownUiTest.java
@@ -26,8 +26,8 @@
 import android.os.PowerManager;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.R;
@@ -41,7 +41,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class ShutdownUiTest extends SysuiTestCase {
 
     ShutdownUi mShutdownUi;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractorTest.kt
index d7a0d5b5..64cd091 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/domain/interactor/KeyboardBacklightInteractorTest.kt
@@ -17,6 +17,7 @@
 
 package com.android.systemui.keyboard.backlight.domain.interactor
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -29,11 +30,10 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class KeyboardBacklightInteractorTest : SysuiTestCase() {
 
     private val keyboardRepository = FakeKeyboardRepository()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinatorTest.kt
index 7207fbf..47261a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinatorTest.kt
@@ -17,6 +17,7 @@
 
 package com.android.systemui.keyboard.backlight.ui
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyboard.backlight.domain.interactor.KeyboardBacklightInteractor
@@ -37,7 +38,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.times
@@ -46,7 +46,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class KeyboardBacklightDialogCoordinatorTest : SysuiTestCase() {
 
     @Mock private lateinit var accessibilityManagerWrapper: AccessibilityManagerWrapper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt
index 4410e68..53bcf86 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt
@@ -21,6 +21,7 @@
 import android.hardware.input.InputManager.KeyboardBacklightListener
 import android.hardware.input.KeyboardBacklightState
 import android.view.InputDevice
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.FlowValue
@@ -44,7 +45,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentCaptor
 import org.mockito.Captor
 import org.mockito.Mock
@@ -53,7 +53,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class KeyboardRepositoryTest : SysuiTestCase() {
 
     @Captor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModelTest.kt
new file mode 100644
index 0000000..3a86868
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/docking/ui/viewmodel/KeyboardDockingIndicationViewModelTest.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.docking.ui.viewmodel
+
+import android.graphics.Rect
+import android.view.WindowInsets
+import android.view.WindowManager
+import android.view.WindowMetrics
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
+import com.android.systemui.keyboard.docking.domain.interactor.KeyboardDockingIndicationInteractor
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mockito.spy
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyboardDockingIndicationViewModelTest : SysuiTestCase() {
+    private val testScope = TestScope(StandardTestDispatcher())
+
+    private lateinit var keyboardRepository: FakeKeyboardRepository
+    private lateinit var configurationRepository: FakeConfigurationRepository
+    private lateinit var underTest: KeyboardDockingIndicationViewModel
+    private lateinit var windowManager: WindowManager
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+
+        keyboardRepository = FakeKeyboardRepository()
+        configurationRepository = FakeConfigurationRepository()
+        windowManager = spy(context.getSystemService(WindowManager::class.java)!!)
+
+        val keyboardDockingIndicationInteractor =
+            KeyboardDockingIndicationInteractor(keyboardRepository)
+        val configurationInteractor = ConfigurationInteractor(configurationRepository)
+
+        underTest =
+            KeyboardDockingIndicationViewModel(
+                windowManager,
+                context,
+                keyboardDockingIndicationInteractor,
+                configurationInteractor,
+                testScope.backgroundScope
+            )
+    }
+
+    @Test
+    fun onConfigurationChanged_createsNewConfig() {
+        val oldBounds = Rect(0, 0, 10, 10)
+        val newBounds = Rect(10, 10, 20, 20)
+        val inset = WindowInsets(Rect(1, 1, 1, 1))
+        val density = 1f
+
+        doReturn(WindowMetrics(oldBounds, inset, density))
+            .whenever(windowManager)
+            .currentWindowMetrics
+
+        val firstGlow = underTest.edgeGlow.value
+
+        testScope.runTest {
+            configurationRepository.onAnyConfigurationChange()
+            // Ensure there's some change in the config so that flow emits the new value.
+            doReturn(WindowMetrics(newBounds, inset, density))
+                .whenever(windowManager)
+                .currentWindowMetrics
+
+            val secondGlow = underTest.edgeGlow.value
+
+            assertThat(firstGlow.hashCode()).isNotEqualTo(secondGlow.hashCode())
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperActivityStarterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperActivityStarterTest.kt
index 05a2ca2..0757ea1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperActivityStarterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/ShortcutHelperActivityStarterTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyboard.shortcut.ui
 
 import android.content.Intent
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyboard.shortcut.fakeShortcutHelperStartActivity
@@ -33,11 +34,10 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class ShortcutHelperActivityStarterTest : SysuiTestCase() {
 
     private val kosmos =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
index 44a8904..34b2aaf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyboard.shortcut.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
@@ -34,11 +35,10 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class ShortcutHelperViewModelTest : SysuiTestCase() {
 
     private val kosmos =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
index 59d8fc3..9a721fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/StickyKeysIndicatorCoordinatorTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyboard.stickykeys.ui
 
 import android.app.Dialog
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyboard.data.repository.FakeStickyKeysRepository
@@ -36,13 +37,12 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class StickyKeysIndicatorCoordinatorTest : SysuiTestCase() {
 
     private lateinit var coordinator: StickyKeysIndicatorCoordinator
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
index d14d72d..9d9e5be62 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/stickykeys/ui/viewmodel/StickyKeysIndicatorViewModelTest.kt
@@ -19,6 +19,7 @@
 import android.hardware.input.InputManager
 import android.hardware.input.StickyModifierState
 import android.provider.Settings.Secure.ACCESSIBILITY_STICKY_KEYS
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -47,14 +48,13 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentCaptor
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class StickyKeysIndicatorViewModelTest : SysuiTestCase() {
 
     private val dispatcher = StandardTestDispatcher()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 36bfa09..90ac05f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -135,6 +135,7 @@
                             .thenReturn(FakeSharedPreferences())
                     },
                 userTracker = userTracker,
+                systemSettings = FakeSettings(),
                 broadcastDispatcher = fakeBroadcastDispatcher,
             )
         val remoteUserSelectionManager =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
index 5b836b6..925ace2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationRotateTextViewControllerTest.java
@@ -36,9 +36,9 @@
 
 import android.content.res.ColorStateList;
 import android.graphics.Color;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.logging.KeyguardLogger;
@@ -56,7 +56,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RunWithLooper
 @SmallTest
 public class KeyguardIndicationRotateTextViewControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationTest.java
index b44fb8e..325bae1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardIndicationTest.java
@@ -24,10 +24,10 @@
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.drawable.Drawable;
-import android.testing.AndroidTestingRunner;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -35,7 +35,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class KeyguardIndicationTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index ce8028c..909acca 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -34,7 +34,6 @@
 import android.net.Uri;
 import android.os.Handler;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
@@ -44,6 +43,7 @@
 import androidx.slice.SliceSpecs;
 import androidx.slice.builders.ListBuilder;
 import androidx.slice.core.SliceQuery;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
@@ -72,7 +72,7 @@
 import java.util.concurrent.TimeUnit;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RunWithLooper
 public class KeyguardSliceProviderTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
index 6ebda4d..128dd23 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -7,7 +7,6 @@
 import android.graphics.Rect
 import android.os.PowerManager
 import android.platform.test.annotations.DisableFlags
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
 import android.view.RemoteAnimationTarget
 import android.view.SurfaceControl
@@ -15,6 +14,7 @@
 import android.view.View
 import android.view.ViewRootImpl
 import android.view.WindowManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardViewController
 import com.android.systemui.Flags
@@ -46,7 +46,7 @@
 import org.mockito.MockitoAnnotations
 import java.util.function.Predicate
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @RunWithLooper
 @SmallTest
 class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
index 977116e..144e634 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
@@ -1,20 +1,15 @@
 package com.android.systemui.keyguard
 
 import android.content.ComponentCallbacks2
-import android.graphics.HardwareRenderer
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.SetFlagsRule
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.flags.EnableSceneContainer
-import com.android.systemui.flags.Flags
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.kosmos.testDispatcher
@@ -26,7 +21,6 @@
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.scene.shared.model.Scenes
 import com.android.systemui.testKosmos
-import com.android.systemui.util.mockito.any
 import com.android.systemui.utils.GlobalWindowManager
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.runCurrent
@@ -43,7 +37,7 @@
 import org.mockito.MockitoAnnotations
 
 @OptIn(ExperimentalCoroutinesApi::class)
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class ResourceTrimmerTest : SysuiTestCase() {
     val kosmos = testKosmos()
@@ -62,52 +56,21 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, true)
         keyguardRepository.setDozeAmount(0f)
         keyguardRepository.setKeyguardGoingAway(false)
-
-        val withDeps =
-            KeyguardInteractorFactory.create(
-                featureFlags = featureFlags,
-                repository = keyguardRepository,
-            )
-        val keyguardInteractor = withDeps.keyguardInteractor
         resourceTrimmer =
             ResourceTrimmer(
-                keyguardInteractor,
-                powerInteractor = powerInteractor,
                 keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor,
                 globalWindowManager = globalWindowManager,
                 applicationScope = testScope.backgroundScope,
                 bgDispatcher = kosmos.testDispatcher,
-                featureFlags = featureFlags,
                 sceneInteractor = kosmos.sceneInteractor,
             )
         resourceTrimmer.start()
     }
 
     @Test
-    @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
-    fun noChange_noOutputChanges() =
-        testScope.runTest {
-            testScope.runCurrent()
-            verifyZeroInteractions(globalWindowManager)
-        }
-
-    @Test
-    @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
-    fun dozeAodDisabled_sleep_trimsMemory() =
-        testScope.runTest {
-            powerInteractor.setAsleepForTest()
-            testScope.runCurrent()
-            verify(globalWindowManager, times(1))
-                .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
-            verify(globalWindowManager, times(1)).trimCaches(HardwareRenderer.CACHE_TRIM_ALL)
-        }
-
-    @Test
-    @DisableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
-    fun dozeAodDisabled_flagDisabled_sleep_doesntTrimMemory() =
+    fun dozeAodDisabled_sleep_doesntTrimMemory() =
         testScope.runTest {
             powerInteractor.setAsleepForTest()
             testScope.runCurrent()
@@ -115,8 +78,7 @@
         }
 
     @Test
-    @DisableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
-    fun dozeEnabled_flagDisabled_sleepWithFullDozeAmount_doesntTrimMemory() =
+    fun dozeEnabled_sleepWithFullDozeAmount_doesntTrimMemory() =
         testScope.runTest {
             keyguardRepository.setDreaming(true)
             keyguardRepository.setDozeAmount(1f)
@@ -126,20 +88,6 @@
         }
 
     @Test
-    @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
-    fun dozeEnabled_sleepWithFullDozeAmount_trimsMemory() =
-        testScope.runTest {
-            keyguardRepository.setDreaming(true)
-            keyguardRepository.setDozeAmount(1f)
-            powerInteractor.setAsleepForTest()
-            testScope.runCurrent()
-            verify(globalWindowManager, times(1))
-                .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
-            verify(globalWindowManager, times(1)).trimCaches(HardwareRenderer.CACHE_TRIM_ALL)
-        }
-
-    @Test
-    @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
     fun dozeEnabled_sleepWithoutFullDozeAmount_doesntTrimMemory() =
         testScope.runTest {
             keyguardRepository.setDreaming(true)
@@ -150,34 +98,6 @@
         }
 
     @Test
-    @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
-    fun aodEnabled_sleepWithFullDozeAmount_trimsMemoryOnce() {
-        testScope.runTest {
-            keyguardRepository.setDreaming(true)
-            keyguardRepository.setDozeAmount(0f)
-            powerInteractor.setAsleepForTest()
-
-            testScope.runCurrent()
-            verifyZeroInteractions(globalWindowManager)
-
-            generateSequence(0f) { it + 0.1f }
-                .takeWhile { it < 1f }
-                .forEach {
-                    keyguardRepository.setDozeAmount(it)
-                    testScope.runCurrent()
-                }
-            verifyZeroInteractions(globalWindowManager)
-
-            keyguardRepository.setDozeAmount(1f)
-            testScope.runCurrent()
-            verify(globalWindowManager, times(1))
-                .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
-            verify(globalWindowManager, times(1)).trimCaches(HardwareRenderer.CACHE_TRIM_ALL)
-        }
-    }
-
-    @Test
-    @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
     fun aodEnabled_deviceWakesHalfWayThrough_doesNotTrimMemory() {
         testScope.runTest {
             keyguardRepository.setDreaming(true)
@@ -209,7 +129,6 @@
     }
 
     @Test
-    @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
     @DisableSceneContainer
     fun keyguardTransitionsToGone_trimsFontCache() =
         testScope.runTest {
@@ -220,12 +139,10 @@
             )
             verify(globalWindowManager, times(1))
                 .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
-            verify(globalWindowManager, times(1)).trimCaches(HardwareRenderer.CACHE_TRIM_FONT)
             verifyNoMoreInteractions(globalWindowManager)
         }
 
     @Test
-    @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
     @EnableSceneContainer
     fun keyguardTransitionsToGone_trimsFontCache_scene_container() =
         testScope.runTest {
@@ -233,38 +150,6 @@
 
             verify(globalWindowManager, times(1))
                 .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
-            verify(globalWindowManager, times(1)).trimCaches(HardwareRenderer.CACHE_TRIM_FONT)
             verifyNoMoreInteractions(globalWindowManager)
         }
-
-    @Test
-    @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
-    @DisableSceneContainer
-    fun keyguardTransitionsToGone_flagDisabled_doesNotTrimFontCache() =
-        testScope.runTest {
-            featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, false)
-            keyguardTransitionRepository.sendTransitionSteps(
-                from = KeyguardState.LOCKSCREEN,
-                to = KeyguardState.GONE,
-                testScope
-            )
-            // Memory hidden should still be called.
-            verify(globalWindowManager, times(1))
-                .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
-            verify(globalWindowManager, times(0)).trimCaches(any())
-        }
-
-    @Test
-    @EnableFlags(com.android.systemui.Flags.FLAG_TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK)
-    @EnableSceneContainer
-    fun keyguardTransitionsToGone_flagDisabled_doesNotTrimFontCache_scene_container() =
-        testScope.runTest {
-            featureFlags.set(Flags.TRIM_FONT_CACHES_AT_UNLOCK, false)
-            kosmos.setSceneTransition(Idle(Scenes.Gone))
-
-            // Memory hidden should still be called.
-            verify(globalWindowManager, times(1))
-                .trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN)
-            verify(globalWindowManager, times(0)).trimCaches(any())
-        }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ScreenLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ScreenLifecycleTest.java
index 70a0415..99ff2d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ScreenLifecycleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ScreenLifecycleTest.java
@@ -21,8 +21,8 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -35,7 +35,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.PrintWriter;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class ScreenLifecycleTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
index 39a453d..a9f7d00 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WakefulnessLifecycleTest.java
@@ -24,8 +24,8 @@
 
 import android.app.IWallpaperManager;
 import android.os.PowerManager;
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -39,7 +39,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.PrintWriter;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class WakefulnessLifecycleTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityTest.kt
index c7f1dec..d435a47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/WorkLockActivityTest.kt
@@ -25,8 +25,8 @@
 import android.os.Looper
 import android.os.UserHandle
 import android.os.UserManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.util.mockito.any
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfigTest.kt
index bd4525b..47bf653 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceConfigTest.kt
@@ -18,16 +18,16 @@
 
 import android.content.Intent
 import android.net.Uri
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
 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.JUnit4
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class KeyguardQuickAffordanceConfigTest : SysuiTestCase() {
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt
index df1833e..5fa194d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt
@@ -203,4 +203,4 @@
         verify(ringerModeInternal).removeObserver(any())
         coroutineContext.cancelChildren()
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt
index f5b5261..972ca02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryTest.kt
@@ -19,6 +19,7 @@
 
 package com.android.systemui.keyguard.data.repository
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.ui.data.repository.ConfigurationRepository
@@ -37,11 +38,10 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class KeyguardBlueprintRepositoryTest : SysuiTestCase() {
     private lateinit var underTest: KeyguardBlueprintRepository
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt
index 9aac8e2..af5187d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardClockRepositoryTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.data.repository
 
 import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.ClockEventController
 import com.android.systemui.SysuiTestCase
@@ -36,11 +37,10 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class KeyguardClockRepositoryTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt
index a320845..8b8a6cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardSmartspaceRepositoryImplTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.data.repository
 
 import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -31,10 +32,9 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.MockitoAnnotations
 
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class KeyguardSmartspaceRepositoryImplTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
index 8be1e7a..a8271c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/DeviceEntrySideFpsOverlayInteractorTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.os.Handler
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardSecurityModel
 import com.android.keyguard.KeyguardUpdateMonitor
@@ -56,7 +57,6 @@
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito.mock
 import org.mockito.junit.MockitoJUnit
@@ -64,7 +64,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class DeviceEntrySideFpsOverlayInteractorTest : SysuiTestCase() {
     @JvmField @Rule var mockitoRule: MockitoRule = MockitoJUnit.rule()
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt
index d3c4848..65aa825 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractorTest.kt
@@ -38,7 +38,7 @@
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.communal.data.repository.fakeCommunalRepository
+import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -110,7 +110,7 @@
     @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
     fun testShowWhenLockedActivity_noLongerOnTop_transitionsToGlanceableHub_ifIdleOnCommunal() =
         testScope.runTest {
-            kosmos.fakeCommunalRepository.setTransitionState(
+            kosmos.fakeCommunalSceneRepository.setTransitionState(
                 flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal))
             )
             runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
index 1839d8d..14f2d65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractorTest.kt
@@ -23,7 +23,7 @@
 import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
-import com.android.systemui.communal.data.repository.fakeCommunalRepository
+import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
 import com.android.systemui.communal.shared.model.CommunalScenes
 import com.android.systemui.coroutines.collectValues
 import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
@@ -170,7 +170,7 @@
     fun testReturnToGlanceableHub_whenBouncerHides_ifIdleOnCommunal() =
         testScope.runTest {
             underTest.start()
-            kosmos.fakeCommunalRepository.setTransitionState(
+            kosmos.fakeCommunalSceneRepository.setTransitionState(
                 flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal))
             )
             bouncerRepository.setPrimaryShow(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
index 0bdf47a..f0ad510 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
@@ -38,6 +38,7 @@
 import com.android.systemui.shade.data.repository.shadeRepository
 import com.android.systemui.shade.shared.model.ShadeMode
 import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -48,6 +49,9 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mock
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
 @ExperimentalCoroutinesApi
@@ -57,6 +61,7 @@
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
     private val underTest = kosmos.keyguardBlueprintInteractor
+    private val keyguardBlueprintRepository = kosmos.keyguardBlueprintRepository
     private val clockRepository by lazy { kosmos.fakeKeyguardClockRepository }
     private val configurationRepository by lazy { kosmos.fakeConfigurationRepository }
     private val fingerprintPropertyRepository by lazy { kosmos.fakeFingerprintPropertyRepository }
@@ -103,20 +108,6 @@
     }
 
     @Test
-    @DisableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
-    fun fingerprintPropertyInitialized_updatesBlueprint() {
-        testScope.runTest {
-            assertThat(kosmos.keyguardBlueprintRepository.targetTransitionConfig).isNull()
-
-            fingerprintPropertyRepository.supportsUdfps() // initialize properties
-
-            runCurrent()
-            advanceUntilIdle()
-            assertThat(kosmos.keyguardBlueprintRepository.targetTransitionConfig).isNotNull()
-        }
-    }
-
-    @Test
     @EnableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
     fun testDoesNotApplySplitShadeBlueprint() {
         testScope.runTest {
@@ -132,15 +123,31 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
+    fun fingerprintPropertyInitialized_updatesBlueprint() {
+        testScope.runTest {
+            underTest.start()
+            reset(keyguardBlueprintRepository)
+
+            fingerprintPropertyRepository.supportsUdfps() // initialize properties
+
+            runCurrent()
+            advanceUntilIdle()
+            verify(keyguardBlueprintRepository, times(2)).refreshBlueprint(any())
+        }
+    }
+
+    @Test
     fun testRefreshFromConfigChange() {
         testScope.runTest {
-            assertThat(kosmos.keyguardBlueprintRepository.targetTransitionConfig).isNull()
+            underTest.start()
+            reset(keyguardBlueprintRepository)
 
             configurationRepository.onConfigurationChange()
 
             runCurrent()
             advanceUntilIdle()
-            assertThat(kosmos.keyguardBlueprintRepository.targetTransitionConfig).isNotNull()
+            verify(keyguardBlueprintRepository, times(2)).refreshBlueprint(any())
         }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 35659c1..d2a9c58 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -63,11 +63,11 @@
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.runner.parameterized.Parameter
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameter
-import org.junit.runners.Parameterized.Parameters
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyString
 import org.mockito.ArgumentMatchers.eq
@@ -83,7 +83,7 @@
     detail = "on certain architectures all permutations with startActivity=true is causing failures"
 )
 @SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 @DisableSceneContainer
 class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() {
 
@@ -281,6 +281,7 @@
                             .thenReturn(FakeSharedPreferences())
                     },
                 userTracker = userTracker,
+                systemSettings = FakeSettings(),
                 broadcastDispatcher = fakeBroadcastDispatcher,
             )
         val remoteUserSelectionManager =
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 ef3183a8..9d06031 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
@@ -63,11 +63,11 @@
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.runner.parameterized.Parameter
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameter
-import org.junit.runners.Parameterized.Parameters
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyString
 import org.mockito.ArgumentMatchers.eq
@@ -83,7 +83,7 @@
     detail = "on certain architectures all permutations with startActivity=true is causing failures"
 )
 @SmallTest
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 @EnableSceneContainer
 class KeyguardQuickAffordanceInteractorSceneContainerTest : SysuiTestCase() {
 
@@ -281,6 +281,7 @@
                             .thenReturn(FakeSharedPreferences())
                     },
                 userTracker = userTracker,
+                systemSettings = FakeSettings(),
                 broadcastDispatcher = fakeBroadcastDispatcher,
             )
         val remoteUserSelectionManager =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListenerTest.kt
index 8a0613f..baa8ed7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardBlueprintCommandListenerTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.view.layout
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.data.repository.KeyguardBlueprintRepository
@@ -28,7 +29,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentMatchers.anyString
 import org.mockito.Mock
 import org.mockito.Mockito.atLeastOnce
@@ -36,7 +36,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class KeyguardBlueprintCommandListenerTest : SysuiTestCase() {
     private lateinit var keyguardBlueprintCommandListener: KeyguardBlueprintCommandListener
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
index 616aac7..9fab0d90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/blueprints/DefaultKeyguardBlueprintTest.kt
@@ -17,10 +17,10 @@
 
 package com.android.systemui.keyguard.ui.view.layout.blueprints
 
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.communal.ui.view.layout.sections.CommunalTutorialIndicatorSection
@@ -44,6 +44,7 @@
 import com.android.systemui.keyguard.ui.view.layout.sections.SplitShadeGuidelines
 import com.android.systemui.util.mockito.whenever
 import java.util.Optional
+import kotlinx.coroutines.ExperimentalCoroutinesApi
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -53,8 +54,9 @@
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @RunWithLooper(setAsMainLooper = true)
+@ExperimentalCoroutinesApi
 @SmallTest
 class DefaultKeyguardBlueprintTest : SysuiTestCase() {
     private lateinit var underTest: DefaultKeyguardBlueprint
@@ -112,17 +114,66 @@
     @Test
     fun replaceViews_withPrevBlueprint() {
         val prevBlueprint = mock(KeyguardBlueprint::class.java)
-        val someSection = mock(KeyguardSection::class.java)
-        whenever(prevBlueprint.sections)
-            .thenReturn(underTest.sections.minus(mDefaultDeviceEntrySection).plus(someSection))
+        val removedSection = mock(KeyguardSection::class.java)
+        val addedSection = mDefaultDeviceEntrySection
+        val rebuildSection = clockSection
+        val prevSections = underTest.sections.minus(addedSection).plus(removedSection)
+        val unchangedSections = underTest.sections.subtract(listOf(addedSection, rebuildSection))
+        whenever(prevBlueprint.sections).thenReturn(prevSections)
+
         val constraintLayout = ConstraintLayout(context, null)
         underTest.replaceViews(constraintLayout, prevBlueprint)
-        underTest.sections.minus(mDefaultDeviceEntrySection).forEach {
-            verify(it, never())?.addViews(constraintLayout)
+
+        unchangedSections.forEach {
+            verify(it, never()).addViews(constraintLayout)
+            verify(it, never()).removeViews(constraintLayout)
         }
 
-        verify(mDefaultDeviceEntrySection).addViews(constraintLayout)
-        verify(someSection).removeViews(constraintLayout)
+        verify(addedSection).addViews(constraintLayout)
+        verify(removedSection).removeViews(constraintLayout)
+    }
+
+    @Test
+    fun replaceViews_withPrevBlueprint_withRebuildTargets() {
+        val prevBlueprint = mock(KeyguardBlueprint::class.java)
+        val removedSection = mock(KeyguardSection::class.java)
+        val addedSection = mDefaultDeviceEntrySection
+        val rebuildSection = clockSection
+        val prevSections = underTest.sections.minus(addedSection).plus(removedSection)
+        val unchangedSections = underTest.sections.subtract(listOf(addedSection, rebuildSection))
+        whenever(prevBlueprint.sections).thenReturn(prevSections)
+
+        val constraintLayout = ConstraintLayout(context, null)
+        underTest.replaceViews(constraintLayout, prevBlueprint, listOf(rebuildSection))
+
+        unchangedSections.forEach {
+            verify(it, never()).addViews(constraintLayout)
+            verify(it, never()).removeViews(constraintLayout)
+        }
+
+        verify(addedSection).addViews(constraintLayout)
+        verify(rebuildSection).addViews(constraintLayout)
+        verify(rebuildSection).removeViews(constraintLayout)
+        verify(removedSection).removeViews(constraintLayout)
+    }
+
+    @Test
+    fun rebuildViews() {
+        val rebuildSections = listOf(mDefaultDeviceEntrySection, clockSection)
+        val unchangedSections = underTest.sections.subtract(rebuildSections)
+
+        val constraintLayout = ConstraintLayout(context, null)
+        underTest.rebuildViews(constraintLayout, rebuildSections)
+
+        unchangedSections.forEach {
+            verify(it, never()).addViews(constraintLayout)
+            verify(it, never()).removeViews(constraintLayout)
+        }
+
+        rebuildSections.forEach {
+            verify(it).addViews(constraintLayout)
+            verify(it).removeViews(constraintLayout)
+        }
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
index b3cc5c9..4a39a9b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
@@ -22,6 +22,7 @@
 import android.view.View.GONE
 import android.view.View.VISIBLE
 import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -52,12 +53,11 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.ArgumentMatchers.anyString
 import org.mockito.MockitoAnnotations
 
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class ClockSectionTest : SysuiTestCase() {
     private lateinit var underTest: ClockSection
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
index 4f2b690..693a877 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
@@ -21,6 +21,7 @@
 import android.view.WindowManager
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.LegacyLockIconViewController
 import com.android.systemui.Flags as AConfigFlags
@@ -44,14 +45,13 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Answers
 import org.mockito.Mock
 import org.mockito.Mockito.mock
 import org.mockito.MockitoAnnotations
 
 @ExperimentalCoroutinesApi
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class DefaultDeviceEntrySectionTest : SysuiTestCase() {
     @Mock private lateinit var authController: AuthController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
index 711f90f..10f7128 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultIndicationAreaSectionTest.kt
@@ -20,6 +20,7 @@
 import android.view.ViewGroup
 import androidx.constraintlayout.widget.ConstraintLayout
 import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags as AConfigFlags
 import com.android.systemui.SysuiTestCase
@@ -30,11 +31,10 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class DefaultIndicationAreaSectionTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
index be10b82..7d4f034 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSectionTest.kt
@@ -22,6 +22,7 @@
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.constraintlayout.widget.ConstraintSet.GONE
 import androidx.constraintlayout.widget.ConstraintSet.VISIBLE
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
@@ -41,11 +42,10 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class SmartspaceSectionTest : SysuiTestCase() {
     private lateinit var underTest: SmartspaceSection
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
index f1c93c4..e44bc7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt
@@ -17,6 +17,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -35,11 +36,10 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mockito.verify
 
 @ExperimentalCoroutinesApi
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class AlternateBouncerViewModelTest : SysuiTestCase() {
     private val kosmos = testKosmos()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
index 391831a..6398a5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerWindowViewModelTest.kt
@@ -17,6 +17,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.keyguardUpdateMonitor
 import com.android.systemui.Flags
@@ -37,11 +38,10 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.kotlin.whenever
 
 @ExperimentalCoroutinesApi
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class AlternateBouncerWindowViewModelTest : SysuiTestCase() {
     private val kosmos = testKosmos()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelTest.kt
index a18b033..129752e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelTest.kt
@@ -17,28 +17,34 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.os.fakeExecutorHandler
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
+import com.android.systemui.testKosmos
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class KeyguardBlueprintViewModelTest : SysuiTestCase() {
     @Mock private lateinit var keyguardBlueprintInteractor: KeyguardBlueprintInteractor
     private lateinit var undertest: KeyguardBlueprintViewModel
+    private val kosmos = testKosmos()
 
     @Before
     fun setup() {
         MockitoAnnotations.initMocks(this)
         undertest =
-            KeyguardBlueprintViewModel(keyguardBlueprintInteractor = keyguardBlueprintInteractor)
+            KeyguardBlueprintViewModel(
+                handler = kosmos.fakeExecutorHandler,
+                keyguardBlueprintInteractor = keyguardBlueprintInteractor,
+            )
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 16421a0..bdc5fc3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -178,6 +178,7 @@
                             .thenReturn(FakeSharedPreferences())
                     },
                 userTracker = userTracker,
+                systemSettings = FakeSettings(),
                 broadcastDispatcher = fakeBroadcastDispatcher,
             )
         val remoteUserSelectionManager =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index 83382207..e33d75c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -20,6 +20,7 @@
 import android.app.admin.DevicePolicyManager
 import android.content.Intent
 import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.LockPatternUtils
 import com.android.systemui.Flags as AConfigFlags
@@ -75,7 +76,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentMatchers
 import org.mockito.Mock
 import org.mockito.Mockito
@@ -83,7 +83,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
 
     @Mock private lateinit var activityStarter: ActivityStarter
@@ -221,6 +221,7 @@
                             .thenReturn(FakeSharedPreferences())
                     },
                 userTracker = userTracker,
+                systemSettings = FakeSettings(),
                 broadcastDispatcher = fakeBroadcastDispatcher,
             )
         val remoteUserSelectionManager =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt
index 78f4dcc..0c3fcb3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardSmartspaceViewModelTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -32,13 +33,12 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Answers
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class KeyguardSmartspaceViewModelTest : SysuiTestCase() {
     val kosmos = testKosmos()
     val testScope = kosmos.testScope
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java
index 447b333..fbeb6d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java
@@ -32,9 +32,9 @@
 import static org.mockito.Mockito.when;
 
 import android.os.RemoteException;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.InstanceId;
@@ -54,7 +54,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 @SmallTest
 public class SessionTrackerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/core/LoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/core/LoggerTest.kt
index ab19b3a..d2e6dad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/core/LoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/core/LoggerTest.kt
@@ -1,5 +1,6 @@
 package com.android.systemui.log.core
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.log.LogMessageImpl
@@ -16,10 +17,9 @@
 import org.mockito.Mockito.isNull
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
-import org.mockito.junit.MockitoJUnitRunner
 
 @SmallTest
-@RunWith(MockitoJUnitRunner::class)
+@RunWith(AndroidJUnit4::class)
 class LoggerTest : SysuiTestCase() {
     @Mock private lateinit var buffer: MessageBuffer
     private lateinit var message: LogMessage
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt
index 5986f4a..e2a2b7a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataFilterImplTest.kt
@@ -18,8 +18,8 @@
 
 import android.app.smartspace.SmartspaceAction
 import android.os.Bundle
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.InstanceId
 import com.android.systemui.SysuiTestCase
@@ -64,7 +64,7 @@
 private val SMARTSPACE_INSTANCE_ID = InstanceId.fakeInstanceId(456)!!
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class LegacyMediaDataFilterImplTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
index 3372f06..bdee936 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt
@@ -40,9 +40,9 @@
 import android.os.Bundle
 import android.provider.Settings
 import android.service.notification.StatusBarNotification
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
 import androidx.media.utils.MediaConstants
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.dx.mockito.inline.extended.ExtendedMockito
 import com.android.internal.logging.InstanceId
@@ -113,7 +113,7 @@
 
 @SmallTest
 @RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class LegacyMediaDataManagerImplTest : SysuiTestCase() {
 
     @JvmField @Rule val mockito = MockitoJUnit.rule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
index dd05a0d..544350c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataCombineLatestTest.java
@@ -26,9 +26,9 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.InstanceId;
@@ -48,7 +48,7 @@
 import java.util.ArrayList;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class MediaDataCombineLatestTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
index caaa42f..8471fe1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt
@@ -18,8 +18,8 @@
 
 import android.app.smartspace.SmartspaceAction
 import android.os.Bundle
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.InstanceId
 import com.android.systemui.SysuiTestCase
@@ -76,7 +76,7 @@
 
 @ExperimentalCoroutinesApi
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class MediaDataFilterImplTest : SysuiTestCase() {
     val kosmos = testKosmos()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
index 3bf4173..18b4c48 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt
@@ -40,10 +40,10 @@
 import android.os.Bundle
 import android.provider.Settings
 import android.service.notification.StatusBarNotification
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
 import androidx.media.utils.MediaConstants
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.dx.mockito.inline.extended.ExtendedMockito
 import com.android.internal.logging.InstanceId
@@ -125,7 +125,7 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class MediaDataProcessorTest : SysuiTestCase() {
     val kosmos = testKosmos()
 
@@ -191,7 +191,7 @@
 
     @Before
     fun setup() {
-        whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+        whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
 
         staticMockSession =
             ExtendedMockito.mockitoSession()
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 d2701dd..42bd46f 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
@@ -29,9 +29,10 @@
 import android.media.session.MediaSession
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
+import android.platform.test.annotations.RequiresFlagsDisabled
 import android.platform.test.flag.junit.DeviceFlagsValueProvider
-import android.testing.AndroidTestingRunner
 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
@@ -40,6 +41,7 @@
 import com.android.settingslib.media.LocalMediaManager
 import com.android.settingslib.media.MediaDevice
 import com.android.settingslib.media.PhoneMediaDevice
+import com.android.settingslib.media.flags.Flags.FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.media.controls.MediaTestUtils
 import com.android.systemui.media.controls.shared.model.MediaData
@@ -84,7 +86,7 @@
 private const val NORMAL_APP_NAME = "NORMAL_APP_NAME"
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 public class MediaDeviceManagerTest : SysuiTestCase() {
     @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
@@ -101,7 +103,7 @@
     @Mock private lateinit var listener: MediaDeviceManager.Listener
     @Mock private lateinit var device: MediaDevice
     @Mock private lateinit var icon: Drawable
-    @Mock private lateinit var route: RoutingSessionInfo
+    @Mock private lateinit var routingSession: RoutingSessionInfo
     @Mock private lateinit var selectedRoute: MediaRoute2Info
     @Mock private lateinit var controller: MediaController
     @Mock private lateinit var playbackInfo: PlaybackInfo
@@ -141,7 +143,10 @@
         whenever(lmmFactory.create(PACKAGE)).thenReturn(lmm)
         whenever(muteAwaitFactory.create(lmm)).thenReturn(muteAwaitManager)
         whenever(lmm.getCurrentConnectedDevice()).thenReturn(device)
-        whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(route)
+        whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(routingSession)
+
+        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_LOCAL)
+        whenever(controller.playbackInfo).thenReturn(playbackInfo)
 
         // Create a media sesssion and notification for testing.
         session = MediaSession(context, SESSION_KEY)
@@ -235,6 +240,7 @@
         reset(listener)
         // WHEN media data is loaded with a different token
         // AND that token results in a null route
+        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
         whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
         manager.onMediaDataLoaded(KEY, null, mediaData)
         fakeBgExecutor.runAllReady()
@@ -394,9 +400,10 @@
     }
 
     @Test
-    fun deviceNameFromMR2RouteInfo() {
+    fun onMediaDataLoaded_withRemotePlaybackType_usesNonNullRoutingSessionName() {
         // GIVEN that MR2Manager returns a valid routing session
-        whenever(route.name).thenReturn(REMOTE_DEVICE_NAME)
+        whenever(routingSession.name).thenReturn(REMOTE_DEVICE_NAME)
+        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
         // WHEN a notification is added
         manager.onMediaDataLoaded(KEY, null, mediaData)
         fakeBgExecutor.runAllReady()
@@ -408,8 +415,9 @@
     }
 
     @Test
-    fun deviceDisabledWhenMR2ReturnsNullRouteInfo() {
+    fun onMediaDataLoaded_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
         // GIVEN that MR2Manager returns null for routing session
+        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
         whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
         // WHEN a notification is added
         manager.onMediaDataLoaded(KEY, null, mediaData)
@@ -422,13 +430,14 @@
     }
 
     @Test
-    fun deviceDisabledWhenMR2ReturnsNullRouteInfoOnDeviceChanged() {
+    fun onSelectedDeviceStateChanged_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
         // GIVEN a notif is added
         manager.onMediaDataLoaded(KEY, null, mediaData)
         fakeBgExecutor.runAllReady()
         fakeFgExecutor.runAllReady()
         reset(listener)
         // AND MR2Manager returns null for routing session
+        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
         whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
         // WHEN the selected device changes state
         val deviceCallback = captureCallback()
@@ -442,13 +451,14 @@
     }
 
     @Test
-    fun deviceDisabledWhenMR2ReturnsNullRouteInfoOnDeviceListUpdate() {
+    fun onDeviceListUpdate_withRemotePlaybackInfo_noMatchingRoutingSession_setsDisabledDevice() {
         // GIVEN a notif is added
         manager.onMediaDataLoaded(KEY, null, mediaData)
         fakeBgExecutor.runAllReady()
         fakeFgExecutor.runAllReady()
         reset(listener)
         // GIVEN that MR2Manager returns null for routing session
+        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
         whenever(mr2.getRoutingSessionForMediaController(any())).thenReturn(null)
         // WHEN the selected device changes state
         val deviceCallback = captureCallback()
@@ -461,15 +471,17 @@
         assertThat(data.name).isNull()
     }
 
+    // With the flag enabled, MediaDeviceManager no longer gathers device name information directly.
+    @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
     @Test
     fun mr2ReturnsSystemRouteWithNullName_isPhone_usePhoneName() {
         // When the routing session name is null, and is a system session for a PhoneMediaDevice
         val phoneDevice = mock(PhoneMediaDevice::class.java)
         whenever(phoneDevice.iconWithoutBackground).thenReturn(icon)
         whenever(lmm.currentConnectedDevice).thenReturn(phoneDevice)
-        whenever(route.isSystemSession).thenReturn(true)
+        whenever(routingSession.isSystemSession).thenReturn(true)
 
-        whenever(route.name).thenReturn(null)
+        whenever(routingSession.name).thenReturn(null)
         whenever(mr2.getSelectedRoutes(any())).thenReturn(listOf(selectedRoute))
         whenever(selectedRoute.name).thenReturn(REMOTE_DEVICE_NAME)
         whenever(selectedRoute.type).thenReturn(MediaRoute2Info.TYPE_BUILTIN_SPEAKER)
@@ -483,13 +495,15 @@
         assertThat(data.name).isEqualTo(PhoneMediaDevice.getMediaTransferThisDeviceName(context))
     }
 
+    // With the flag enabled, MediaDeviceManager no longer gathers device name information directly.
+    @RequiresFlagsDisabled(FLAG_USE_PLAYBACK_INFO_FOR_ROUTING_CONTROLS)
     @Test
     fun mr2ReturnsSystemRouteWithNullName_useSelectedRouteName() {
         // When the routing session does not have a name, and is a system session
-        whenever(route.name).thenReturn(null)
+        whenever(routingSession.name).thenReturn(null)
         whenever(mr2.getSelectedRoutes(any())).thenReturn(listOf(selectedRoute))
         whenever(selectedRoute.name).thenReturn(REMOTE_DEVICE_NAME)
-        whenever(route.isSystemSession).thenReturn(true)
+        whenever(routingSession.isSystemSession).thenReturn(true)
 
         manager.onMediaDataLoaded(KEY, null, mediaData)
         fakeBgExecutor.runAllReady()
@@ -503,8 +517,8 @@
     @Test
     fun mr2ReturnsNonSystemRouteWithNullName_useLocalDeviceName() {
         // GIVEN that MR2Manager returns a routing session that does not have a name
-        whenever(route.name).thenReturn(null)
-        whenever(route.isSystemSession).thenReturn(false)
+        whenever(routingSession.name).thenReturn(null)
+        whenever(routingSession.isSystemSession).thenReturn(false)
         // WHEN a notification is added
         manager.onMediaDataLoaded(KEY, null, mediaData)
         fakeBgExecutor.runAllReady()
@@ -534,7 +548,7 @@
     }
 
     @Test
-    fun audioInfoVolumeControlIdChanged() {
+    fun onAudioInfoChanged_withRemotePlaybackInfo_queriesRoutingSession() {
         whenever(playbackInfo.getPlaybackType()).thenReturn(PlaybackInfo.PLAYBACK_TYPE_LOCAL)
         whenever(playbackInfo.getVolumeControlId()).thenReturn(null)
         whenever(controller.getPlaybackInfo()).thenReturn(playbackInfo)
@@ -545,10 +559,11 @@
         reset(mr2)
         // WHEN onAudioInfoChanged fires with a volume control id change
         whenever(playbackInfo.getVolumeControlId()).thenReturn("placeholder id")
+        whenever(playbackInfo.playbackType).thenReturn(PlaybackInfo.PLAYBACK_TYPE_REMOTE)
         val captor = ArgumentCaptor.forClass(MediaController.Callback::class.java)
         verify(controller).registerCallback(captor.capture())
         captor.value.onAudioInfoChanged(playbackInfo)
-        // THEN the route is checked
+        // THEN the routing session is checked
         verify(mr2).getRoutingSessionForMediaController(eq(controller))
     }
 
@@ -654,7 +669,7 @@
 
     @Test
     fun testRemotePlaybackDeviceOverride() {
-        whenever(route.name).thenReturn(DEVICE_NAME)
+        whenever(routingSession.name).thenReturn(DEVICE_NAME)
         val deviceData =
             MediaDeviceData(false, null, REMOTE_DEVICE_NAME, null, showBroadcastButton = false)
         val mediaDataWithDevice = mediaData.copy(device = deviceData)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
index 31a2435..efe4413 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaSessionBasedFilterTest.kt
@@ -20,8 +20,8 @@
 import android.media.session.MediaController.PlaybackInfo
 import android.media.session.MediaSession
 import android.media.session.MediaSessionManager
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.media.controls.MediaTestUtils
@@ -53,7 +53,7 @@
     MediaTestUtils.emptyMediaData.copy(packageName = PACKAGE, notificationKey = NOTIF_KEY)
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 public class MediaSessionBasedFilterTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
index 6ca0bef..c1bba4d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaTimeoutListenerTest.kt
@@ -20,7 +20,7 @@
 import android.media.session.MediaController
 import android.media.session.MediaSession
 import android.media.session.PlaybackState
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.media.controls.MediaTestUtils
@@ -65,7 +65,7 @@
 }
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class MediaTimeoutListenerTest : SysuiTestCase() {
 
     @Mock private lateinit var mediaControllerFactory: MediaControllerFactory
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt
index 55ff231..02d7413 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/MediaResumeListenerTest.kt
@@ -27,8 +27,8 @@
 import android.media.MediaDescription
 import android.media.session.MediaSession
 import android.provider.Settings
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
@@ -76,7 +76,7 @@
 private fun <T> any(): T = Mockito.any<T>()
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class MediaResumeListenerTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserTest.kt
index 8dfa5b8..dca19690 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/resume/ResumeMediaBrowserTest.kt
@@ -23,8 +23,8 @@
 import android.media.session.MediaController
 import android.media.session.MediaSession
 import android.service.media.MediaBrowserService
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.mockito.mock
@@ -53,7 +53,7 @@
 private fun <T> any(): T = Mockito.any<T>()
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 public class ResumeMediaBrowserTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaPlayerDataTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaPlayerDataTest.kt
index b509e77..addf008 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaPlayerDataTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaPlayerDataTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.media.controls.ui
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.media.controls.MediaTestUtils
@@ -34,7 +34,7 @@
 import org.mockito.junit.MockitoJUnit
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 public class MediaPlayerDataTest : SysuiTestCase() {
 
     @Mock private lateinit var playerIsPlaying: MediaControlPanel
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandlerTest.kt
index 4fcd3bb..cdcb143 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/AnimationBindHandlerTest.kt
@@ -18,8 +18,8 @@
 
 import android.graphics.drawable.Animatable2
 import android.graphics.drawable.Drawable
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import junit.framework.Assert.assertFalse
@@ -37,7 +37,7 @@
 import org.mockito.junit.MockitoJUnit
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class AnimationBindHandlerTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt
index aa297b5..4d0605f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/ColorSchemeTransitionTest.kt
@@ -18,8 +18,8 @@
 
 import android.animation.ValueAnimator
 import android.graphics.Color
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.media.controls.ui.view.GutsViewHolder
@@ -44,7 +44,7 @@
 private const val TARGET_COLOR = Color.BLUE
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class ColorSchemeTransitionTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandlerTest.kt
index bb95ba3..2499c9c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/animation/MetadataAnimationHandlerTest.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.media.controls.ui.animation
 
 import android.animation.Animator
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import junit.framework.Assert.fail
@@ -36,7 +36,7 @@
 import org.mockito.junit.MockitoJUnit
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class MetadataAnimationHandlerTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
index 8a6b272..4e14fec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/binder/SeekBarObserverTest.kt
@@ -18,11 +18,11 @@
 
 import android.animation.Animator
 import android.animation.ObjectAnimator
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
 import android.widget.SeekBar
 import android.widget.TextView
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.media.controls.ui.drawable.SquigglyProgress
@@ -40,7 +40,7 @@
 import org.mockito.junit.MockitoJUnit
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class SeekBarObserverTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt
index 791563a..2f95936 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/KeyguardMediaControllerTest.kt
@@ -17,11 +17,11 @@
 package com.android.systemui.media.controls.ui.controller
 
 import android.provider.Settings
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View.GONE
 import android.view.View.VISIBLE
 import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
@@ -50,7 +50,7 @@
 import org.mockito.junit.MockitoJUnit
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class KeyguardMediaControllerTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
index a89139b..f7b3f2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
@@ -22,10 +22,10 @@
 import android.database.ContentObserver
 import android.os.LocaleList
 import android.provider.Settings
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.util.MathUtils.abs
 import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.InstanceId
 import com.android.keyguard.KeyguardUpdateMonitor
@@ -40,6 +40,7 @@
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.media.controls.MediaTestUtils
@@ -107,7 +108,7 @@
 
 @SmallTest
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class MediaCarouselControllerTest : SysuiTestCase() {
     val kosmos = testKosmos()
 
@@ -158,6 +159,7 @@
         testDispatcher = UnconfinedTestDispatcher()
         mediaCarouselController =
             MediaCarouselController(
+                applicationScope = kosmos.applicationCoroutineScope,
                 context = context,
                 mediaControlPanelFactory = mediaControlPanelFactory,
                 visualStabilityProvider = visualStabilityProvider,
@@ -194,7 +196,7 @@
         whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false)
         MediaPlayerData.clear()
         verify(globalSettings)
-            .registerContentObserver(
+            .registerContentObserverSync(
                 eq(Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE)),
                 capture(settingsObserverCaptor)
             )
@@ -893,7 +895,10 @@
             mediaCarouselController.updateHostVisibility = { updatedVisibility = true }
             mediaCarouselController.mediaCarousel = mediaCarousel
 
-            val settingsJob = mediaCarouselController.listenForLockscreenSettingChanges(this)
+            val settingsJob =
+                mediaCarouselController.listenForLockscreenSettingChanges(
+                    kosmos.applicationCoroutineScope
+                )
             secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, false)
 
             val keyguardJob = mediaCarouselController.listenForAnyStateToLockscreenTransition(this)
@@ -920,7 +925,10 @@
             mediaCarouselController.updateHostVisibility = { updatedVisibility = true }
             mediaCarouselController.mediaCarousel = mediaCarousel
 
-            val settingsJob = mediaCarouselController.listenForLockscreenSettingChanges(this)
+            val settingsJob =
+                mediaCarouselController.listenForLockscreenSettingChanges(
+                    kosmos.applicationCoroutineScope
+                )
             secureSettings.putBool(Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN, true)
 
             val keyguardJob = mediaCarouselController.listenForAnyStateToLockscreenTransition(this)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
index 6d7976e..ecc456c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaControlPanelTest.kt
@@ -45,7 +45,6 @@
 import android.platform.test.flag.junit.DeviceFlagsValueProvider
 import android.provider.Settings
 import android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.util.TypedValue
 import android.view.View
@@ -60,6 +59,7 @@
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.lifecycle.LiveData
 import androidx.media.utils.MediaConstants
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.InstanceId
 import com.android.internal.widget.CachingIconView
@@ -137,7 +137,7 @@
 private const val APP_NAME = "APP_NAME"
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class MediaControlPanelTest : SysuiTestCase() {
     @get:Rule val checkFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
index 5f7c386..bba01bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManagerTest.kt
@@ -18,10 +18,10 @@
 
 import android.graphics.Rect
 import android.provider.Settings
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.ViewGroup
 import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardViewController
 import com.android.systemui.SysuiTestCase
@@ -29,7 +29,9 @@
 import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
 import com.android.systemui.dreams.DreamOverlayStateController
 import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.media.controls.domain.pipeline.MediaDataManager
@@ -49,7 +51,6 @@
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController
 import com.android.systemui.testKosmos
 import com.android.systemui.util.animation.UniqueObjectHostView
-import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.nullable
 import com.android.systemui.util.settings.FakeSettings
@@ -75,10 +76,12 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when` as whenever
 import org.mockito.junit.MockitoJUnit
+import org.mockito.kotlin.any
+import org.mockito.kotlin.anyOrNull
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class MediaHierarchyManagerTest : SysuiTestCase() {
 
@@ -112,12 +115,14 @@
     private val testScope = kosmos.testScope
     private lateinit var mediaHierarchyManager: MediaHierarchyManager
     private lateinit var isQsBypassingShade: MutableStateFlow<Boolean>
+    private lateinit var shadeExpansion: MutableStateFlow<Float>
     private lateinit var mediaFrame: ViewGroup
     private val configurationController = FakeConfigurationController()
     private val settings = FakeSettings()
     private lateinit var testableLooper: TestableLooper
     private lateinit var fakeHandler: FakeHandler
     private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
+    private val keyguardRepository = kosmos.fakeKeyguardRepository
 
     @Before
     fun setup() {
@@ -129,7 +134,9 @@
         fakeHandler = FakeHandler(testableLooper.looper)
         whenever(mediaCarouselController.mediaFrame).thenReturn(mediaFrame)
         isQsBypassingShade = MutableStateFlow(false)
+        shadeExpansion = MutableStateFlow(0f)
         whenever(shadeInteractor.isQsBypassingShade).thenReturn(isQsBypassingShade)
+        whenever(shadeInteractor.shadeExpansion).thenReturn(shadeExpansion)
         whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(false)
         mediaHierarchyManager =
             MediaHierarchyManager(
@@ -141,6 +148,7 @@
                 mediaDataManager,
                 keyguardViewController,
                 dreamOverlayStateController,
+                kosmos.keyguardInteractor,
                 kosmos.communalTransitionViewModel,
                 configurationController,
                 wakefulnessLifecycle,
@@ -191,7 +199,7 @@
         verify(mediaCarouselController)
             .onDesiredLocationChanged(
                 ArgumentMatchers.anyInt(),
-                any(MediaHostState::class.java),
+                any<MediaHostState>(),
                 anyBoolean(),
                 anyLong(),
                 anyLong()
@@ -204,7 +212,7 @@
         verify(mediaCarouselController, times(0))
             .onDesiredLocationChanged(
                 ArgumentMatchers.anyInt(),
-                any(MediaHostState::class.java),
+                any<MediaHostState>(),
                 anyBoolean(),
                 anyLong(),
                 anyLong()
@@ -218,7 +226,7 @@
         verify(mediaCarouselController)
             .onDesiredLocationChanged(
                 ArgumentMatchers.anyInt(),
-                any(MediaHostState::class.java),
+                any<MediaHostState>(),
                 anyBoolean(),
                 anyLong(),
                 anyLong()
@@ -231,7 +239,7 @@
         verify(mediaCarouselController, times(0))
             .onDesiredLocationChanged(
                 ArgumentMatchers.anyInt(),
-                any(MediaHostState::class.java),
+                any<MediaHostState>(),
                 anyBoolean(),
                 anyLong(),
                 anyLong()
@@ -245,7 +253,7 @@
         verify(mediaCarouselController)
             .onDesiredLocationChanged(
                 ArgumentMatchers.anyInt(),
-                any(MediaHostState::class.java),
+                any<MediaHostState>(),
                 anyBoolean(),
                 anyLong(),
                 anyLong()
@@ -255,7 +263,7 @@
         verify(mediaCarouselController)
             .onDesiredLocationChanged(
                 ArgumentMatchers.anyInt(),
-                any(MediaHostState::class.java),
+                any<MediaHostState>(),
                 anyBoolean(),
                 anyLong(),
                 anyLong()
@@ -269,7 +277,7 @@
         verify(mediaCarouselController)
             .onDesiredLocationChanged(
                 ArgumentMatchers.anyInt(),
-                any(MediaHostState::class.java),
+                any<MediaHostState>(),
                 anyBoolean(),
                 anyLong(),
                 anyLong()
@@ -281,7 +289,7 @@
         verify(mediaCarouselController)
             .onDesiredLocationChanged(
                 ArgumentMatchers.anyInt(),
-                any(MediaHostState::class.java),
+                any<MediaHostState>(),
                 anyBoolean(),
                 anyLong(),
                 anyLong()
@@ -297,7 +305,7 @@
         verify(mediaCarouselController)
             .onDesiredLocationChanged(
                 eq(MediaHierarchyManager.LOCATION_QQS),
-                any(MediaHostState::class.java),
+                any<MediaHostState>(),
                 eq(false),
                 anyLong(),
                 anyLong()
@@ -309,7 +317,7 @@
         verify(mediaCarouselController)
             .onDesiredLocationChanged(
                 eq(MediaHierarchyManager.LOCATION_LOCKSCREEN),
-                any(MediaHostState::class.java),
+                any<MediaHostState>(),
                 eq(false),
                 anyLong(),
                 anyLong()
@@ -482,6 +490,26 @@
     }
 
     @Test
+    fun isCurrentlyInGuidedTransformation_desiredLocationIsHub_returnsFalse() =
+        testScope.runTest {
+            goToLockscreen()
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.GLANCEABLE_HUB,
+                testScope = testScope,
+            )
+            mediaHierarchyManager.qsExpansion = 0f
+            mediaHierarchyManager.setTransitionToFullShadeAmount(123f)
+
+            whenever(lockHost.visible).thenReturn(true)
+            whenever(qsHost.visible).thenReturn(true)
+            whenever(qqsHost.visible).thenReturn(true)
+            whenever(hubModeHost.visible).thenReturn(true)
+
+            assertThat(mediaHierarchyManager.isCurrentlyInGuidedTransformation()).isFalse()
+        }
+
+    @Test
     fun testDream() {
         goToDream()
         setMediaDreamComplicationEnabled(true)
@@ -499,7 +527,7 @@
         verify(mediaCarouselController)
             .onDesiredLocationChanged(
                 eq(MediaHierarchyManager.LOCATION_QQS),
-                any(MediaHostState::class.java),
+                any<MediaHostState>(),
                 eq(false),
                 anyLong(),
                 anyLong()
@@ -532,7 +560,7 @@
             verify(mediaCarouselController)
                 .onDesiredLocationChanged(
                     eq(MediaHierarchyManager.LOCATION_QQS),
-                    any(MediaHostState::class.java),
+                    any<MediaHostState>(),
                     eq(false),
                     anyLong(),
                     anyLong()
@@ -590,7 +618,50 @@
             verify(mediaCarouselController)
                 .onDesiredLocationChanged(
                     eq(MediaHierarchyManager.LOCATION_QS),
-                    any(MediaHostState::class.java),
+                    any<MediaHostState>(),
+                    eq(false),
+                    anyLong(),
+                    anyLong()
+                )
+        }
+
+    @Test
+    fun testCommunalLocation_whenDreamingAndShadeExpanding() =
+        testScope.runTest {
+            keyguardRepository.setDreaming(true)
+            runCurrent()
+            keyguardTransitionRepository.sendTransitionSteps(
+                from = KeyguardState.DREAMING,
+                to = KeyguardState.GLANCEABLE_HUB,
+                testScope = testScope,
+            )
+            // Mock the behavior for dreaming that pulling down shade will immediately set QS as
+            // expanded
+            expandQS()
+            // Starts opening the shade
+            shadeExpansion.value = 0.1f
+            runCurrent()
+
+            // UMO shows on hub
+            verify(mediaCarouselController)
+                .onDesiredLocationChanged(
+                    eq(MediaHierarchyManager.LOCATION_COMMUNAL_HUB),
+                    anyOrNull(),
+                    eq(false),
+                    anyLong(),
+                    anyLong()
+                )
+            clearInvocations(mediaCarouselController)
+
+            // The shade is opened enough to make QS elements visible
+            shadeExpansion.value = 0.5f
+            runCurrent()
+
+            // UMO shows on QS
+            verify(mediaCarouselController)
+                .onDesiredLocationChanged(
+                    eq(MediaHierarchyManager.LOCATION_QS),
+                    any<MediaHostState>(),
                     eq(false),
                     anyLong(),
                     anyLong()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
index e5d3082..00b9a46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt
@@ -23,7 +23,6 @@
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.GradientDrawable
 import android.graphics.drawable.RippleDrawable
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
 import android.view.ViewGroup
@@ -35,6 +34,7 @@
 import android.widget.TextView
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.lifecycle.LiveData
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.widget.CachingIconView
 import com.android.systemui.SysuiTestCase
@@ -72,7 +72,7 @@
 
 @SmallTest
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class MediaViewControllerTest : SysuiTestCase() {
     private val mediaHostStateHolder = MediaHost.MediaHostStateHolder()
     private val mediaHostStatesManager = MediaHostStatesManager()
@@ -376,7 +376,7 @@
 
     @Test
     fun attachPlayer_seekBarDisabled_seekBarVisibilityIsSetToInvisible() {
-        whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+        whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
 
         mediaViewController.attachPlayer(viewHolder)
         getEnabledChangeListener().onEnabledChanged(enabled = true)
@@ -388,7 +388,7 @@
 
     @Test
     fun attachPlayer_seekBarEnabled_seekBarVisible() {
-        whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+        whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
 
         mediaViewController.attachPlayer(viewHolder)
         getEnabledChangeListener().onEnabledChanged(enabled = true)
@@ -399,7 +399,7 @@
 
     @Test
     fun attachPlayer_seekBarStatusUpdate_seekBarVisibilityChanges() {
-        whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+        whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
 
         mediaViewController.attachPlayer(viewHolder)
         getEnabledChangeListener().onEnabledChanged(enabled = true)
@@ -415,7 +415,7 @@
 
     @Test
     fun attachPlayer_notScrubbing_scrubbingViewsGone() {
-        whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+        whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
 
         mediaViewController.attachPlayer(viewHolder)
         mediaViewController.canShowScrubbingTime = true
@@ -435,7 +435,7 @@
 
     @Test
     fun setIsScrubbing_noSemanticActions_scrubbingViewsGone() {
-        whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+        whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
 
         mediaViewController.attachPlayer(viewHolder)
         mediaViewController.canShowScrubbingTime = false
@@ -454,7 +454,7 @@
 
     @Test
     fun setIsScrubbing_noPrevButton_scrubbingTimesNotShown() {
-        whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+        whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
 
         mediaViewController.attachPlayer(viewHolder)
         mediaViewController.setUpNextButtonInfo(true)
@@ -476,7 +476,7 @@
 
     @Test
     fun setIsScrubbing_noNextButton_scrubbingTimesNotShown() {
-        whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+        whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
 
         mediaViewController.attachPlayer(viewHolder)
         mediaViewController.setUpNextButtonInfo(false)
@@ -498,7 +498,7 @@
 
     @Test
     fun setIsScrubbing_scrubbingViewsShownAndPrevNextHiddenOnlyInExpanded() {
-        whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+        whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
 
         mediaViewController.attachPlayer(viewHolder)
         mediaViewController.setUpNextButtonInfo(true)
@@ -524,7 +524,7 @@
 
     @Test
     fun setIsScrubbing_trueThenFalse_reservePrevAndNextButtons() {
-        whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true)
+        whenever(mediaFlags.isSceneContainerEnabled()).thenReturn(true)
 
         mediaViewController.attachPlayer(viewHolder)
         mediaViewController.setUpNextButtonInfo(true, ConstraintSet.INVISIBLE)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgressTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgressTest.kt
index 0319aaa..e87f176 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgressTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/drawable/SquigglyProgressTest.kt
@@ -21,8 +21,8 @@
 import android.graphics.LightingColorFilter
 import android.graphics.Paint
 import android.graphics.Rect
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.graphics.ColorUtils
 import com.android.systemui.SysuiTestCase
@@ -40,7 +40,7 @@
 import org.mockito.junit.MockitoJUnit
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class SquigglyProgressTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt
index 1208369..d073cf1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaCarouselScrollHandlerTest.kt
@@ -16,9 +16,9 @@
 
 package com.android.systemui.media.controls.ui.view
 
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.MotionEvent
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.media.controls.util.MediaUiEventLogger
@@ -38,7 +38,7 @@
 
 @SmallTest
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class MediaCarouselScrollHandlerTest : SysuiTestCase() {
 
     private val carouselWidth = 1038
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaViewHolderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaViewHolderTest.kt
index d3c703c..cdb060c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaViewHolderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/view/MediaViewHolderTest.kt
@@ -16,17 +16,17 @@
 
 package com.android.systemui.media.controls.ui.view
 
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.LayoutInflater
 import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class MediaViewHolderTest : SysuiTestCase() {
 
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 e1c2d3f..4da7b2a 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
@@ -20,12 +20,12 @@
 import android.media.session.MediaController
 import android.media.session.MediaSession
 import android.media.session.PlaybackState
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.MotionEvent
 import android.widget.SeekBar
 import androidx.arch.core.executor.ArchTaskExecutor
 import androidx.arch.core.executor.TaskExecutor
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.Classifier
@@ -53,7 +53,7 @@
 import org.mockito.junit.MockitoJUnit
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class SeekBarViewModelTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/util/MediaDataUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/util/MediaDataUtilsTest.kt
index 86f3062..bb9d20f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/util/MediaDataUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/util/MediaDataUtilsTest.kt
@@ -16,8 +16,8 @@
 
 package com.android.systemui.media.controls.util
 
-import android.testing.AndroidTestingRunner
 import android.util.Pair as APair
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
@@ -25,7 +25,7 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class MediaDataUtilsTest : SysuiTestCase() {
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 95e34a9..ec02c64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -35,13 +35,13 @@
 import android.app.WallpaperColors;
 import android.graphics.Bitmap;
 import android.graphics.drawable.Icon;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.View;
 import android.widget.LinearLayout;
 import android.widget.SeekBar;
 
 import androidx.core.graphics.drawable.IconCompat;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.settingslib.media.LocalMediaManager;
@@ -62,7 +62,7 @@
 import java.util.stream.Collectors;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class MediaOutputAdapterTest extends SysuiTestCase {
 
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 9616f610..d5cd86e 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
@@ -34,7 +34,6 @@
 import android.media.session.PlaybackState;
 import android.os.Bundle;
 import android.os.PowerExemptionManager;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.View;
 import android.widget.Button;
@@ -42,6 +41,7 @@
 import android.widget.TextView;
 
 import androidx.core.graphics.drawable.IconCompat;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
@@ -67,7 +67,7 @@
 import java.util.List;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class MediaOutputBaseDialogTest extends SysuiTestCase {
 
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 16b00c0..87d2245 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
@@ -33,13 +33,13 @@
 import android.media.AudioManager;
 import android.media.session.MediaSessionManager;
 import android.os.PowerExemptionManager;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.View;
 import android.widget.EditText;
 import android.widget.ImageView;
 import android.widget.TextView;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -72,7 +72,7 @@
 import java.util.List;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class MediaOutputBroadcastDialogTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 45ae506..856bc0b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -59,12 +59,12 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
 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.settingslib.bluetooth.CachedBluetoothDeviceManager;
@@ -95,7 +95,7 @@
 import java.util.List;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class MediaOutputControllerTest extends SysuiTestCase {
     private static final String TEST_DEVICE_1_ID = "test_device_1_id";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java
index 1e8fbea..50ae25c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogReceiverTest.java
@@ -27,8 +27,8 @@
 import android.content.Intent;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.settingslib.flags.Flags;
@@ -40,7 +40,7 @@
 import org.junit.runner.RunWith;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class MediaOutputDialogReceiverTest extends SysuiTestCase {
 
     private MediaOutputDialogReceiver mMediaOutputDialogReceiver;
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 92d0a72..f20b04a 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
@@ -38,12 +38,12 @@
 import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.FeatureFlagUtils;
 import android.view.View;
 
 import androidx.annotation.NonNull;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.MediumTest;
 
 import com.android.internal.logging.UiEventLogger;
@@ -75,7 +75,7 @@
 import java.util.function.Consumer;
 
 @MediumTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class MediaOutputDialogTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java
index a828843..8e9a1f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaComplicationViewControllerTest.java
@@ -19,9 +19,9 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.verify;
 
-import android.testing.AndroidTestingRunner;
 import android.widget.FrameLayout;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -35,7 +35,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class MediaComplicationViewControllerTest extends SysuiTestCase {
     @Mock
     private MediaHost mMediaHost;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
index 8f8630e..a49819e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
@@ -25,8 +25,8 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -44,7 +44,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class MediaDreamSentinelTest extends SysuiTestCase {
     @Mock
     MediaDataManager mMediaDataManager;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 27f59d29..f1d833f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -23,13 +23,13 @@
 import android.media.MediaRoute2Info
 import android.os.Handler
 import android.os.PowerManager
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
 import android.view.ViewGroup
 import android.view.WindowManager
 import android.view.accessibility.AccessibilityManager
 import android.widget.ImageView
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.InstanceId
 import com.android.internal.logging.testing.UiEventLoggerFake
@@ -59,7 +59,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class MediaTttChipControllerReceiverTest : SysuiTestCase() {
     private lateinit var controllerReceiver: MediaTttChipControllerReceiver
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
index 3be50b1..111b8d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderCoordinatorTest.kt
@@ -24,7 +24,6 @@
 import android.os.PowerManager
 import android.os.VibrationAttributes
 import android.os.VibrationEffect
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
 import android.view.ViewGroup
@@ -32,6 +31,7 @@
 import android.view.accessibility.AccessibilityManager
 import android.widget.ImageView
 import android.widget.TextView
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.internal.statusbar.IUndoMediaTransferCallback
@@ -74,7 +74,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class MediaTttSenderCoordinatorTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt
index da448aa..6238257 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/MediaProjectionMetricsLoggerTest.kt
@@ -1,7 +1,7 @@
 package com.android.systemui.mediaprojection
 
 import android.media.projection.IMediaProjectionManager
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_APP as METRICS_CREATION_SOURCE_APP
 import com.android.internal.util.FrameworkStatsLog.MEDIA_PROJECTION_STATE_CHANGED__CREATION_SOURCE__CREATION_SOURCE_CAST as METRICS_CREATION_SOURCE_CAST
@@ -13,7 +13,7 @@
 import org.junit.runner.RunWith
 import org.mockito.Mockito.verify
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class MediaProjectionMetricsLoggerTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
index 2536078..22bdfe8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
@@ -2,7 +2,7 @@
 
 import android.content.ComponentName
 import android.os.UserHandle
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
@@ -24,7 +24,7 @@
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class MediaProjectionAppSelectorControllerTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt
index db36131..a73df07 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ActivityTaskManagerThumbnailLoaderTest.kt
@@ -7,9 +7,9 @@
 import android.graphics.Point
 import android.graphics.Rect
 import android.hardware.HardwareBuffer
-import android.testing.AndroidTestingRunner
 import android.view.Surface
 import android.window.TaskSnapshot
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.shared.recents.model.ThumbnailData
@@ -24,7 +24,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 @OptIn(ExperimentalCoroutinesApi::class)
 class ActivityTaskManagerThumbnailLoaderTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/BasicPackageManagerAppIconLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/BasicPackageManagerAppIconLoaderTest.kt
index fa1c8f8..a0cd835 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/BasicPackageManagerAppIconLoaderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/BasicPackageManagerAppIconLoaderTest.kt
@@ -20,6 +20,7 @@
 import android.content.pm.ActivityInfo
 import android.content.pm.PackageManager
 import android.graphics.Bitmap
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.launcher3.icons.FastBitmapDrawable
 import com.android.systemui.SysuiTestCase
@@ -31,10 +32,9 @@
 import kotlinx.coroutines.runBlocking
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class BasicPackageManagerAppIconLoaderTest : SysuiTestCase() {
 
     private val packageManagerWrapper: PackageManagerWrapper = mock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
index 6ac86f5..2f61579 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
@@ -3,7 +3,7 @@
 import android.app.ActivityManager.RecentTaskInfo
 import android.content.pm.UserInfo
 import android.os.UserManager
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.mediaprojection.appselector.data.RecentTask.UserType.CLONED
@@ -25,7 +25,7 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentMatchers.anyInt
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class ShellRecentTaskListProviderTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
similarity index 90%
rename from packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepositoryTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
index 6043ede..b7fefc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/MediaProjectionManagerRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2023 The Android Open Source Project
+ * Copyright (C) 2024 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,28 +14,27 @@
  * limitations under the License.
  */
 
-package com.android.systemui.mediaprojection.taskswitcher.data.repository
+package com.android.systemui.mediaprojection.data.repository
 
 import android.os.Binder
-import android.testing.AndroidTestingRunner
 import android.view.ContentRecordingSession
+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.mediaprojection.data.model.MediaProjectionState
 import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
 import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createToken
-import com.android.systemui.mediaprojection.taskswitcher.data.model.MediaProjectionState
 import com.android.systemui.mediaprojection.taskswitcher.fakeActivityTaskManager
 import com.android.systemui.mediaprojection.taskswitcher.fakeMediaProjectionManager
-import com.android.systemui.mediaprojection.taskswitcher.mediaProjectionManagerRepository
 import com.android.systemui.mediaprojection.taskswitcher.taskSwitcherKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class MediaProjectionManagerRepositoryTest : SysuiTestCase() {
 
@@ -45,7 +44,7 @@
     private val fakeMediaProjectionManager = kosmos.fakeMediaProjectionManager
     private val fakeActivityTaskManager = kosmos.fakeActivityTaskManager
 
-    private val repo = kosmos.mediaProjectionManagerRepository
+    private val repo = kosmos.realMediaProjectionRepository
 
     @Test
     fun switchProjectedTask_stateIsUpdatedWithNewTask() =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolverTest.kt
index c63efa1..ea5603d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolverTest.kt
@@ -25,10 +25,11 @@
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertWithMessage
 import org.junit.Before
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
+import platform.test.runner.parameterized.Parameter
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
-import org.junit.runners.Parameterized.Parameters
 import org.mockito.ArgumentMatchers.any
 
 abstract class BaseScreenCaptureDevicePolicyResolverTest(private val precondition: Preconditions) :
@@ -81,7 +82,7 @@
     }
 }
 
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 @SmallTest
 class IsAllowedScreenCaptureDevicePolicyResolverTest(
     private val test: IsScreenCaptureAllowedTestCase
@@ -468,7 +469,7 @@
     }
 }
 
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 @SmallTest
 class IsCompletelyNotAllowedScreenCaptureDevicePolicyResolverTest(
     private val test: IsScreenCaptureCompletelyDisabledTestCase
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt
index e044eec..548366e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt
@@ -18,11 +18,11 @@
 
 import android.app.AlertDialog
 import android.media.projection.MediaProjectionConfig
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.WindowManager
 import android.widget.Spinner
 import android.widget.TextView
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FeatureFlagsClassic
@@ -40,7 +40,7 @@
 import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class MediaProjectionPermissionDialogDelegateTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt
index 16c92ec..8fe8878 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/MediaProjectionTaskSwitcherCoreStartableTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.mediaprojection.taskswitcher
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags.FLAG_PSS_TASK_SWITCHER
 import com.android.systemui.SysuiTestCase
@@ -27,7 +27,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class MediaProjectionTaskSwitcherCoreStartableTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepositoryTest.kt
index bda0e1e..d3ce871 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/data/repository/ActivityTaskManagerTasksRepositoryTest.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.mediaprojection.taskswitcher.data.repository
 
 import android.os.Binder
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -32,7 +32,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class ActivityTaskManagerTasksRepositoryTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractorTest.kt
index 33e65f26..7fe55b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractorTest.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.mediaprojection.taskswitcher.domain.interactor
 
 import android.content.Intent
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -35,7 +35,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class TaskSwitchInteractorTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt
index 9382c58..7417dac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt
@@ -18,7 +18,7 @@
 
 import android.app.Notification
 import android.app.NotificationManager
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.kosmos.testScope
@@ -40,9 +40,10 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.Mockito.never
+import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class TaskSwitcherNotificationCoordinatorTest : SysuiTestCase() {
 
@@ -66,6 +67,10 @@
                 fakeBroadcastDispatcher,
             )
         coordinator.start()
+        // When the coordinator starts up, the view model will immediately emit a NotShowing event
+        // and hide the notification. That's fine, but we should reset the notification manager so
+        // that the initial emission isn't part of the tests.
+        reset(notificationManager)
     }
 
     @Test
@@ -82,8 +87,13 @@
     @Test
     fun hideNotification() {
         testScope.runTest {
+            // First, show a notification
+            switchTask()
+
+            // WHEN the projection is stopped
             fakeMediaProjectionManager.dispatchOnStop()
 
+            // THEN the notification is hidden
             verify(notificationManager).cancel(any(), any())
         }
     }
@@ -91,14 +101,16 @@
     @Test
     fun notificationIdIsConsistent() {
         testScope.runTest {
-            fakeMediaProjectionManager.dispatchOnStop()
-            val idCancel = argumentCaptor<Int>()
-            verify(notificationManager).cancel(any(), idCancel.capture())
-
+            // First, show a notification
             switchTask()
             val idNotify = argumentCaptor<Int>()
             verify(notificationManager).notify(any(), idNotify.capture(), any())
 
+            // Then, hide the notification
+            fakeMediaProjectionManager.dispatchOnStop()
+            val idCancel = argumentCaptor<Int>()
+            verify(notificationManager).cancel(any(), idCancel.capture())
+
             assertEquals(idCancel.value, idNotify.value)
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModelTest.kt
index a468953..5bedc13 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/viewmodel/TaskSwitcherNotificationViewModelTest.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.mediaprojection.taskswitcher.ui.viewmodel
 
 import android.content.Intent
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -37,7 +37,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class TaskSwitcherNotificationViewModelTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt
index c06a28e..a3be9e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateExtTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.model
 
 import android.view.Display
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.settings.FakeDisplayTracker
@@ -24,10 +25,9 @@
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class SysUiStateExtTest : SysuiTestCase() {
 
     private val kosmos = testKosmos()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java
index f03f4f7..9a78bd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/model/SysUiStateTest.java
@@ -23,6 +23,7 @@
 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;
@@ -32,10 +33,9 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.junit.MockitoJUnitRunner;
 
 @SmallTest
-@RunWith(MockitoJUnitRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class SysUiStateTest extends SysuiTestCase {
     private static final int FLAG_1 = 1;
     private static final int FLAG_2 = 1 << 1;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java
index 3eb7329..85244fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarButtonTest.java
@@ -27,12 +27,12 @@
 import android.hardware.display.VirtualDisplay;
 import android.media.ImageReader;
 import android.os.SystemClock;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 import android.util.Log;
 import android.view.Display;
 import android.view.DisplayInfo;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -53,7 +53,7 @@
 import java.util.function.Predicate;
 
 /** atest NavigationBarButtonTest */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RunWithLooper
 @SmallTest
 public class NavigationBarButtonTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
index 354a87a..d5361ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerImplTest.java
@@ -39,9 +39,9 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
-import android.testing.AndroidTestingRunner;
 import android.util.SparseArray;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.dx.mockito.inline.extended.StaticMockitoSession;
@@ -72,7 +72,7 @@
 import java.util.Optional;
 
 /** atest NavigationBarControllerTest */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class NavigationBarControllerImplTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarInflaterViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarInflaterViewTest.java
index 52d02b6..a358c18 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarInflaterViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarInflaterViewTest.java
@@ -22,12 +22,12 @@
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 import android.util.SparseArray;
 import android.view.View;
 import android.widget.FrameLayout;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -41,7 +41,7 @@
 import org.junit.runner.RunWith;
 
 /** atest NavigationBarInflaterViewTest */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RunWithLooper
 @SmallTest
 public class NavigationBarInflaterViewTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 6cea1e8..2b60f65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -57,7 +57,6 @@
 import android.os.SystemClock;
 import android.provider.DeviceConfig;
 import android.telecom.TelecomManager;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.Display;
@@ -73,6 +72,7 @@
 import android.view.accessibility.AccessibilityManager;
 import android.view.inputmethod.InputMethodManager;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.MetricsLogger;
@@ -132,7 +132,7 @@
 import java.util.Optional;
 import java.util.concurrent.Executor;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class NavigationBarTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
index fb08bf5..fbfd35f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java
@@ -25,9 +25,9 @@
 import static org.mockito.Mockito.when;
 
 import android.content.Context;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -46,7 +46,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RunWithLooper
 @SmallTest
 public class NavigationBarTransitionsTest extends SysuiTestCase {
@@ -105,4 +105,4 @@
 
         assertTrue(mTransitions.isLightsOut(BarTransitions.MODE_LIGHTS_OUT));
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java
index a1010a0..841f620 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/KeyButtonViewTest.java
@@ -40,11 +40,11 @@
 import static org.mockito.Mockito.verify;
 
 import android.hardware.input.InputManagerGlobal;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.KeyEvent;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.MetricsLogger;
@@ -60,7 +60,7 @@
 import org.mockito.Captor;
 import org.mockito.MockitoAnnotations;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RunWithLooper
 @SmallTest
 public class KeyButtonViewTest extends SysuiTestCase {
@@ -164,4 +164,4 @@
             verify(mUiEventLogger, times(1)).log(expected);
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/NearestTouchFrameTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/NearestTouchFrameTest.java
index 038b42b..6a3c615 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/NearestTouchFrameTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/buttons/NearestTouchFrameTest.java
@@ -28,11 +28,11 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.graphics.Rect;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.MotionEvent;
 import android.view.View;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -46,7 +46,7 @@
 
 import java.util.Map;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RunWithLooper
 @SmallTest
 public class NearestTouchFrameTest extends SysuiTestCase {
@@ -276,4 +276,4 @@
         v.setBottom(height);
         return v;
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
index f1c97dd..b169cc1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/BackPanelControllerTest.kt
@@ -17,7 +17,6 @@
 package com.android.systemui.navigationbar.gestural
 
 import android.os.Handler
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.HapticFeedbackConstants
 import android.view.MotionEvent
@@ -26,6 +25,7 @@
 import android.view.MotionEvent.ACTION_UP
 import android.view.ViewConfiguration
 import android.view.WindowManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.jank.Cuj
 import com.android.internal.util.LatencyTracker
@@ -49,12 +49,13 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class BackPanelControllerTest : SysuiTestCase() {
     companion object {
         private const val START_X: Float = 0f
     }
+
     private val kosmos = testKosmos()
     private lateinit var mBackPanelController: BackPanelController
     private lateinit var systemClock: FakeSystemClock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt
index 5f206b3..f3cea3e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt
@@ -8,10 +8,12 @@
 import com.android.systemui.shared.rotation.FloatingRotationButtonPositionCalculator.Position
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameter
+import platform.test.runner.parameterized.Parameters
 import org.junit.runner.RunWith
-import org.junit.runners.Parameterized
 
-@RunWith(Parameterized::class)
+@RunWith(ParameterizedAndroidJunit4::class)
 @SmallTest
 internal class FloatingRotationButtonPositionCalculatorTest(
         private val testCase: TestCase,
@@ -61,7 +63,7 @@
             MARGIN_DEFAULT, MARGIN_TASKBAR_LEFT, MARGIN_TASKBAR_BOTTOM, false
         )
 
-        @Parameterized.Parameters(name = "{0}")
+        @Parameters(name = "{0}")
         @JvmStatic
         fun getParams(): Collection<TestCase> =
             listOf(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt
index bdb095a..0f13f03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt
@@ -18,8 +18,8 @@
 
 import android.content.Context
 import android.content.Intent
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import androidx.test.rule.ActivityTestRule
 import com.android.dx.mockito.inline.extended.ExtendedMockito.verify
@@ -37,7 +37,7 @@
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 @TestableLooper.RunWithLooper
 class LaunchNotesRoleSettingsTrampolineActivityTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt
index 06127a7..9ef6b9c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskBubblesServiceTest.kt
@@ -19,8 +19,8 @@
 import android.content.Intent
 import android.graphics.drawable.Icon
 import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
 import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.notetask.NoteTaskBubblesController.NoteTaskBubblesService
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index b7618d2..0196f95 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -42,9 +42,9 @@
 import android.os.UserHandle
 import android.os.UserManager
 import android.provider.Settings
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.ext.truth.content.IntentSubject.assertThat
 import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.notetask.NoteTaskController.Companion.EXTRA_SHORTCUT_BADGE_OVERRIDE_PACKAGE
 import com.android.systemui.notetask.NoteTaskController.Companion.SHORTCUT_ID
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt
index 4101c94..c9a5d06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt
@@ -16,8 +16,8 @@
 package com.android.systemui.notetask
 
 import android.os.UserHandle
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt
index 2c86a8d..0c88da7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt
@@ -19,8 +19,8 @@
 import android.app.role.RoleManager
 import android.content.pm.ApplicationInfo
 import android.content.pm.PackageManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.whenever
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
index 24f39d1..8f4078b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt
@@ -16,7 +16,7 @@
 package com.android.systemui.notetask
 
 import android.os.UserHandle
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT_IN_MULTI_WINDOW_MODE
@@ -26,7 +26,7 @@
 
 /** atest SystemUITests:NoteTaskInfoTest */
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 internal class NoteTaskInfoTest : SysuiTestCase() {
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
index 7833007..1ec4814 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
@@ -19,12 +19,12 @@
 import android.app.role.RoleManager.ROLE_NOTES
 import android.os.UserHandle
 import android.os.UserManager
-import android.testing.AndroidTestingRunner
 import android.view.KeyEvent
 import android.view.KeyEvent.ACTION_DOWN
 import android.view.KeyEvent.ACTION_UP
 import android.view.KeyEvent.KEYCODE_N
 import android.view.KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.systemui.SysuiTestCase
@@ -54,7 +54,7 @@
 /** atest SystemUITests:NoteTaskInitializerTest */
 @OptIn(ExperimentalCoroutinesApi::class, InternalNoteTaskApi::class)
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 internal class NoteTaskInitializerTest : SysuiTestCase() {
 
     @Mock lateinit var commandQueue: CommandQueue
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
index 231b333..f624f20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt
@@ -25,7 +25,7 @@
 import android.hardware.input.InputSettings
 import android.os.UserHandle
 import android.os.UserManager
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.dx.mockito.inline.extended.ExtendedMockito
 import com.android.systemui.SysuiTestCase
@@ -63,7 +63,7 @@
 
 /** atest SystemUITests:NoteTaskQuickAffordanceConfigTest */
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() {
 
     @Mock lateinit var controller: NoteTaskController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivityTest.kt
index 1f0f0d7..9969dcd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivityTest.kt
@@ -16,8 +16,8 @@
 
 package com.android.systemui.notetask.shortcut
 
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import androidx.test.rule.ActivityTestRule
 import com.android.dx.mockito.inline.extended.ExtendedMockito.verify
@@ -35,7 +35,7 @@
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 @TestableLooper.RunWithLooper
 class LaunchNoteTaskActivityTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/NotificationHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/NotificationHelperTest.java
index 1b713dd..3673a25 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/NotificationHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/NotificationHelperTest.java
@@ -36,8 +36,8 @@
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.util.ArrayUtils;
@@ -52,7 +52,7 @@
 import java.util.List;
 import java.util.Set;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class NotificationHelperTest extends SysuiTestCase {
     private static final String SHORTCUT_ID_1 = "101";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleBackupFollowUpJobTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleBackupFollowUpJobTest.java
index 0d1749c..dae3452 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleBackupFollowUpJobTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleBackupFollowUpJobTest.java
@@ -35,8 +35,8 @@
 import android.net.Uri;
 import android.os.RemoteException;
 import android.preference.PreferenceManager;
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -56,7 +56,7 @@
 import java.util.Map;
 import java.util.Set;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class PeopleBackupFollowUpJobTest extends SysuiTestCase {
     private static final String SHORTCUT_ID_1 = "101";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTest.java
index 50ab1c7..776cf19 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleProviderTest.java
@@ -29,9 +29,9 @@
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
 import android.widget.RemoteViews;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -46,7 +46,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class PeopleProviderTest extends SysuiTestCase {
     private static final String TAG = "PeopleProviderTest";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
index 84a8ab0..48afaa0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleSpaceUtilsTest.java
@@ -52,9 +52,9 @@
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.provider.ContactsContract;
-import android.testing.AndroidTestingRunner;
 import android.util.DisplayMetrics;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.appwidget.IAppWidgetService;
@@ -81,7 +81,7 @@
 import java.util.Map;
 import java.util.Optional;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class PeopleSpaceUtilsTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
index 3d1da00..a90c10a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/PeopleTileViewHelperTest.java
@@ -52,12 +52,12 @@
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
 import android.util.DisplayMetrics;
 import android.view.View;
 import android.widget.RemoteViews;
 import android.widget.TextView;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -73,7 +73,7 @@
 import java.time.Duration;
 import java.util.Arrays;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class PeopleTileViewHelperTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/SharedPreferencesHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/SharedPreferencesHelperTest.java
index 7cd5e22..ae7fba9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/SharedPreferencesHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/SharedPreferencesHelperTest.java
@@ -24,8 +24,8 @@
 
 import android.content.Context;
 import android.content.SharedPreferences;
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -34,7 +34,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class SharedPreferencesHelperTest extends SysuiTestCase {
     private static final String SHORTCUT_ID_1 = "101";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
index c8ebd12..e701dc6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/LaunchConversationActivityTest.java
@@ -36,9 +36,9 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.service.notification.NotificationListenerService;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.statusbar.IStatusBarService;
@@ -64,7 +64,7 @@
 import java.util.Optional;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RunWithLooper
 public class LaunchConversationActivityTest extends SysuiTestCase {
     private static final String EMPTY_STRING = "";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleBackupHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleBackupHelperTest.java
index 5d526e1..b3ded15 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleBackupHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleBackupHelperTest.java
@@ -36,8 +36,8 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.preference.PreferenceManager;
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -57,7 +57,7 @@
 import java.util.Map;
 import java.util.Set;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class PeopleBackupHelperTest extends SysuiTestCase {
     private static final String SHORTCUT_ID_1 = "101";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
index 0998c0c..56a2adc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/people/widget/PeopleSpaceWidgetManagerTest.java
@@ -99,10 +99,10 @@
 import android.service.notification.ConversationChannelWrapper;
 import android.service.notification.StatusBarNotification;
 import android.service.notification.ZenModeConfig;
-import android.testing.AndroidTestingRunner;
 import android.text.TextUtils;
 
 import androidx.preference.PreferenceManager;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -145,7 +145,7 @@
 import java.util.stream.Collectors;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class PeopleSpaceWidgetManagerTest extends SysuiTestCase {
     private static final long MIN_LINGER_DURATION = 5;
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
index b95d3aa..bdd8dc8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
@@ -42,9 +42,9 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.UiEventLogger;
@@ -70,7 +70,7 @@
 import java.lang.ref.WeakReference;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class PowerNotificationWarningsTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index cae170f..4f4f0d9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -38,11 +38,11 @@
 import android.provider.Settings;
 import android.service.vr.IVrManager;
 import android.service.vr.IVrStateCallbacks;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.testing.TestableResources;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.settingslib.fuelgauge.Estimate;
@@ -64,7 +64,7 @@
 import java.time.Duration;
 import java.util.concurrent.TimeUnit;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RunWithLooper
 @SmallTest
 public class PowerUITest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt
index f3b114d..02a3429 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/data/repository/PowerRepositoryImplTest.kt
@@ -21,6 +21,7 @@
 import android.content.Intent
 import android.content.IntentFilter
 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.broadcast.BroadcastDispatcher
@@ -37,7 +38,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentCaptor
 import org.mockito.Captor
 import org.mockito.Mock
@@ -48,7 +48,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class PowerRepositoryImplTest : SysuiTestCase() {
 
     private val systemClock = FakeSystemClock()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
index 42cf9f4..12c9eb9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt
@@ -18,6 +18,7 @@
 package com.android.systemui.power.domain.interactor
 
 import android.os.PowerManager
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
@@ -39,13 +40,12 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class PowerInteractorTest : SysuiTestCase() {
 
     private lateinit var underTest: PowerInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitorTest.kt
index 14ecf93..4bee924 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitorTest.kt
@@ -19,8 +19,8 @@
 import android.app.AppOpsManager
 import android.content.pm.UserInfo
 import android.os.UserHandle
-import android.testing.AndroidTestingRunner
 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.appops.AppOpItem
@@ -52,7 +52,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 @RunWithLooper
 class AppOpsPrivacyItemMonitorTest : SysuiTestCase() {
@@ -385,4 +385,4 @@
         `when`(privacyConfig.locationAvailable).thenReturn(value)
         argCaptorConfigCallback.value.onFlagLocationChanged(value)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt
index dcee5a716..0d6652c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt
@@ -16,8 +16,8 @@
 
 package com.android.systemui.privacy
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import androidx.test.runner.AndroidJUnit4
 import com.android.systemui.SysuiTestCase
 import org.junit.Assert.assertEquals
 import org.junit.Test
@@ -74,4 +74,4 @@
         val appList = textBuilder.appsAndTypes.map { it.first }.map { it.packageName }
         assertEquals(listOf("Camera", "Microphone", "Location"), appList)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyConfigFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyConfigFlagsTest.kt
index 272f149..4768b88 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyConfigFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyConfigFlagsTest.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.privacy
 
 import android.provider.DeviceConfig
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
 import com.android.systemui.SysuiTestCase
@@ -37,7 +37,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class PrivacyConfigFlagsTest : SysuiTestCase() {
     companion object {
@@ -146,4 +146,4 @@
                 false
         )
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
index 84e9107..58afcb7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt
@@ -30,7 +30,7 @@
 import android.os.UserHandle
 import android.permission.PermissionGroupUsage
 import android.permission.PermissionManager
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
@@ -64,7 +64,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class PrivacyDialogControllerTest : SysuiTestCase() {
 
     companion object {
@@ -824,4 +824,4 @@
         `when`(usage.proxyLabel).thenReturn(proxyLabel)
         return usage
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerV2Test.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerV2Test.kt
index 0c7e099..1c63161 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerV2Test.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerV2Test.kt
@@ -30,8 +30,8 @@
 import android.os.UserHandle
 import android.permission.PermissionGroupUsage
 import android.permission.PermissionManager
-import android.testing.AndroidTestingRunner
 import android.widget.LinearLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
@@ -66,7 +66,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class PrivacyDialogControllerV2Test : SysuiTestCase() {
 
     companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
index b754145..9ac04cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt
@@ -16,11 +16,11 @@
 
 package com.android.systemui.privacy
 
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
 import android.view.ViewGroup
 import android.widget.TextView
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
@@ -38,7 +38,7 @@
 import android.text.TextUtils
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class PrivacyDialogTest : SysuiTestCase() {
 
@@ -401,4 +401,4 @@
 
         assertThat(TextUtils.isEmpty(dialog.window?.attributes?.title)).isFalse()
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogV2Test.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogV2Test.kt
index 6c01ba5..f7cf458 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogV2Test.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogV2Test.kt
@@ -17,12 +17,12 @@
 package com.android.systemui.privacy
 
 import android.content.Intent
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
 import android.view.ViewGroup
 import android.view.ViewGroup.LayoutParams.MATCH_PARENT
 import android.widget.TextView
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
@@ -38,7 +38,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class PrivacyDialogV2Test : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
index d563632..4f1fb71 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt
@@ -18,8 +18,8 @@
 
 import android.app.ActivityManager
 import android.os.UserHandle
-import android.testing.AndroidTestingRunner
 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.dump.DumpManager
@@ -49,7 +49,7 @@
 import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 @RunWithLooper
 class PrivacyItemControllerTest : SysuiTestCase() {
@@ -417,4 +417,4 @@
 
         assertTrue(privacyItemController.privacyList.isEmpty())
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/process/condition/SystemProcessConditionTest.java b/packages/SystemUI/tests/src/com/android/systemui/process/condition/SystemProcessConditionTest.java
index f573358..5250d56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/process/condition/SystemProcessConditionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/process/condition/SystemProcessConditionTest.java
@@ -19,9 +19,9 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -39,7 +39,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 @SmallTest
 public class SystemProcessConditionTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
index e905e9c..8ccaf6b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerControllerTest.java
@@ -34,9 +34,9 @@
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
@@ -56,7 +56,7 @@
 import java.util.Collections;
 import java.util.List;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 @SmallTest
 public class QRCodeScannerControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
index dee1cc8..1eeaef7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/AutoAddTrackerTest.java
@@ -30,9 +30,9 @@
 import android.content.IntentFilter;
 import android.os.UserHandle;
 import android.provider.Settings.Secure;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -54,7 +54,7 @@
 import java.util.List;
 import java.util.concurrent.Executor;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RunWithLooper
 @SmallTest
 public class AutoAddTrackerTest extends SysuiTestCase {
@@ -308,4 +308,4 @@
                 user
         );
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java
index d39a635..16ae466 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java
@@ -43,9 +43,9 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
@@ -73,7 +73,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 @SmallTest
 public class FgsManagerControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
index f98b68f..3ae7a16 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt
@@ -6,8 +6,8 @@
 import android.content.IntentFilter
 import android.permission.PermissionManager
 import android.safetycenter.SafetyCenterManager
-import android.testing.AndroidTestingRunner
 import android.view.View
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
@@ -46,7 +46,7 @@
 private fun <T> any(): T = Mockito.any<T>()
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class HeaderPrivacyIconsControllerTest : SysuiTestCase() {
 
     @Mock
@@ -269,4 +269,4 @@
         whenever(privacyItemController.micCameraAvailable).thenReturn(micCamera)
         whenever(privacyItemController.locationAvailable).thenReturn(location)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/LeftRightArrowPressedListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/LeftRightArrowPressedListenerTest.kt
index 40eccad..466a09b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/LeftRightArrowPressedListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/LeftRightArrowPressedListenerTest.kt
@@ -16,11 +16,11 @@
 
 package com.android.systemui.qs
 
-import android.testing.AndroidTestingRunner
 import android.view.KeyEvent
 import android.view.KeyEvent.KEYCODE_DPAD_LEFT
 import android.view.View
 import androidx.core.util.Consumer
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.google.common.truth.Truth.assertThat
@@ -28,7 +28,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class LeftRightArrowPressedListenerTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/PagedTileLayoutTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/PagedTileLayoutTest.kt
index 8ef3f57..db8612a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/PagedTileLayoutTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/PagedTileLayoutTest.kt
@@ -1,9 +1,9 @@
 package com.android.systemui.qs
 
 import android.content.Context
-import android.testing.AndroidTestingRunner
 import android.view.View
 import android.widget.Scroller
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.qs.PageIndicator.PageScrollActionListener
@@ -19,7 +19,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class PagedTileLayoutTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSContainerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSContainerImplTest.kt
index 8c5d99a..52ad931 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSContainerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSContainerImplTest.kt
@@ -1,9 +1,9 @@
 package com.android.systemui.qs
 
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.View
 import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
@@ -18,7 +18,7 @@
 import org.mockito.MockitoAnnotations
 import org.mockito.Mockito.`when` as whenever
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 @SmallTest
 class QSContainerImplTest : SysuiTestCase() {
@@ -70,4 +70,4 @@
                 eq(originalPadding)
             )
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
index 5ae0c24..fb58b90 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
@@ -27,11 +27,11 @@
 
 import android.content.ClipData;
 import android.content.ClipboardManager;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.View;
 import android.widget.TextView;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.plugins.ActivityStarter;
@@ -49,7 +49,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 @SmallTest
 public class QSFooterViewControllerTest extends LeakCheckedTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
index 6956bea..c0d390a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSImplTest.java
@@ -39,11 +39,9 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
-import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Bundle;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.Display;
 import android.view.LayoutInflater;
@@ -51,19 +49,19 @@
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
+import androidx.compose.ui.platform.ComposeView;
 import androidx.lifecycle.Lifecycle;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.BouncerPanelExpansionCalculator;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.EnableSceneContainer;
-import com.android.systemui.flags.FeatureFlagsClassic;
 import com.android.systemui.media.controls.ui.view.MediaHost;
 import com.android.systemui.qs.customize.QSCustomizerController;
 import com.android.systemui.qs.dagger.QSComponent;
 import com.android.systemui.qs.external.TileServiceRequestController;
-import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder;
 import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.res.R;
@@ -84,7 +82,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class QSImplTest extends SysuiTestCase {
@@ -112,9 +110,7 @@
     @Mock private QSSquishinessController mSquishinessController;
     @Mock private FooterActionsViewModel mFooterActionsViewModel;
     @Mock private FooterActionsViewModel.Factory mFooterActionsViewModelFactory;
-    @Mock private FooterActionsViewBinder mFooterActionsViewBinder;
     @Mock private LargeScreenShadeInterpolator mLargeScreenShadeInterpolator;
-    @Mock private FeatureFlagsClassic mFeatureFlags;
     private ViewGroup mQsView;
 
     private final CommandQueue mCommandQueue =
@@ -259,6 +255,39 @@
     }
 
     @Test
+    public void setQsExpansion_whenShouldUpdateSquishinessTrue_setsSquishinessBasedOnFraction() {
+        enableSplitShade();
+        when(mStatusBarStateController.getState()).thenReturn(KEYGUARD);
+        float expansion = 0.456f;
+        float panelExpansionFraction = 0.678f;
+        float proposedTranslation = 567f;
+        float squishinessFraction = 0.789f;
+
+        mUnderTest.setShouldUpdateSquishinessOnMedia(true);
+        mUnderTest.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
+                squishinessFraction);
+
+        verify(mQSMediaHost).setSquishFraction(squishinessFraction);
+    }
+
+    @Test
+    public void setQsExpansion_whenOnKeyguardAndShouldUpdateSquishinessFalse_setsSquishiness() {
+        // Random test values without any meaning. They just have to be different from each other.
+        float expansion = 0.123f;
+        float panelExpansionFraction = 0.321f;
+        float proposedTranslation = 456f;
+        float squishinessFraction = 0.567f;
+
+        enableSplitShade();
+        setStatusBarCurrentAndUpcomingState(KEYGUARD);
+        mUnderTest.setShouldUpdateSquishinessOnMedia(false);
+        mUnderTest.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
+                squishinessFraction);
+
+        verify(mQSMediaHost).setSquishFraction(1.0f);
+    }
+
+    @Test
     public void setQsExpansion_inSplitShade_setsFooterActionsExpansion_basedOnPanelExpFraction() {
         // Random test values without any meaning. They just have to be different from each other.
         float expansion = 0.123f;
@@ -496,18 +525,13 @@
     @Test
     @EnableSceneContainer
     public void testSceneContainerFlagsEnabled_FooterActionsRemoved_controllerNotStarted() {
-        clearInvocations(
-                mFooterActionsViewBinder, mFooterActionsViewModel, mFooterActionsViewModelFactory);
+        clearInvocations(mFooterActionsViewModel, mFooterActionsViewModelFactory);
         QSImpl other = instantiate();
 
         other.onComponentCreated(mQsComponent, null);
 
         assertThat((View) other.getView().findViewById(R.id.qs_footer_actions)).isNull();
-        verifyZeroInteractions(
-                mFooterActionsViewModel,
-                mFooterActionsViewBinder,
-                mFooterActionsViewModelFactory
-        );
+        verifyZeroInteractions(mFooterActionsViewModel, mFooterActionsViewModelFactory);
     }
 
     @Test
@@ -553,9 +577,7 @@
                 mock(QSLogger.class),
                 mock(FooterActionsController.class),
                 mFooterActionsViewModelFactory,
-                mFooterActionsViewBinder,
-                mLargeScreenShadeInterpolator,
-                mFeatureFlags
+                mLargeScreenShadeInterpolator
         );
     }
 
@@ -589,41 +611,20 @@
         customizer.setId(android.R.id.edit);
         mQsView.addView(customizer);
 
-        View footerActionsView = new FooterActionsViewBinder().create(mContext);
+        ComposeView footerActionsView = new ComposeView(mContext);
         footerActionsView.setId(R.id.qs_footer_actions);
         mQsView.addView(footerActionsView);
     }
 
     private void setUpInflater() {
-        LayoutInflater realInflater = LayoutInflater.from(mContext);
-
         when(mLayoutInflater.cloneInContext(any(Context.class))).thenReturn(mLayoutInflater);
         when(mLayoutInflater.inflate(anyInt(), nullable(ViewGroup.class), anyBoolean()))
-                .thenAnswer((invocation) -> inflate(realInflater, (int) invocation.getArgument(0),
-                        (ViewGroup) invocation.getArgument(1),
-                        (boolean) invocation.getArgument(2)));
+                .thenAnswer((invocation) -> mQsView);
         when(mLayoutInflater.inflate(anyInt(), nullable(ViewGroup.class)))
-                .thenAnswer((invocation) -> inflate(realInflater, (int) invocation.getArgument(0),
-                        (ViewGroup) invocation.getArgument(1)));
+                .thenAnswer((invocation) -> mQsView);
         mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE, mLayoutInflater);
     }
 
-    private View inflate(LayoutInflater realInflater, int layoutRes, @Nullable ViewGroup root) {
-        return inflate(realInflater, layoutRes, root, root != null);
-    }
-
-    private View inflate(LayoutInflater realInflater, int layoutRes, @Nullable ViewGroup root,
-            boolean attachToRoot) {
-        if (layoutRes == R.layout.footer_actions
-                || layoutRes == R.layout.footer_actions_text_button
-                || layoutRes == R.layout.footer_actions_number_button
-                || layoutRes == R.layout.footer_actions_icon_button) {
-            return realInflater.inflate(layoutRes, root, attachToRoot);
-        }
-
-        return mQsView;
-    }
-
     private void setupQsComponent() {
         when(mQsComponent.getQSPanelController()).thenReturn(mQSPanelController);
         when(mQsComponent.getQuickQSPanelController()).thenReturn(mQuickQSPanelController);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseSceneContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseSceneContainerTest.kt
new file mode 100644
index 0000000..07ec38e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseSceneContainerTest.kt
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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
+
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.testing.TestableLooper.RunWithLooper
+import android.view.ViewTreeObserver
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.haptics.qs.QSLongPressEffect
+import com.android.systemui.haptics.qs.qsLongPressEffect
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.lifecycle.InstantTaskExecutorRule
+import com.android.systemui.media.controls.ui.view.MediaHost
+import com.android.systemui.qs.customize.QSCustomizerController
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.statusbar.policy.SplitShadeStateController
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import javax.inject.Provider
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+@RunWithLooper
+@OptIn(ExperimentalCoroutinesApi::class)
+@EnableSceneContainer
+class QSPanelControllerBaseSceneContainerTest : SysuiTestCase() {
+
+    @Rule @JvmField val mInstantTaskExecutor = InstantTaskExecutorRule()
+
+    private val kosmos = testKosmos()
+
+    @Mock private lateinit var qsPanel: QSPanel
+    @Mock private lateinit var qsHost: QSHost
+    @Mock private lateinit var qsCustomizerController: QSCustomizerController
+    @Mock private lateinit var metricsLogger: MetricsLogger
+    private val uiEventLogger = UiEventLoggerFake()
+    @Mock private lateinit var qsLogger: QSLogger
+    private val dumpManager = DumpManager()
+    @Mock private lateinit var tileLayout: PagedTileLayout
+    @Mock private lateinit var resources: Resources
+    private val configuration = Configuration()
+    @Mock private lateinit var viewTreeObserver: ViewTreeObserver
+    @Mock private lateinit var mediaHost: MediaHost
+
+    private var isSplitShade = false
+    private val splitShadeStateController =
+        object : SplitShadeStateController {
+            override fun shouldUseSplitNotificationShade(resources: Resources): Boolean {
+                return isSplitShade
+            }
+        }
+    private val longPressEffectProvider: Provider<QSLongPressEffect> = Provider {
+        kosmos.qsLongPressEffect
+    }
+
+    private val mediaVisible = MutableStateFlow(false)
+
+    private lateinit var underTest: TestableQSPanelControllerBase
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        allowTestableLooperAsMainThread()
+        Dispatchers.setMain(kosmos.testDispatcher)
+
+        whenever(qsPanel.isAttachedToWindow).thenReturn(true)
+        whenever(qsPanel.orCreateTileLayout).thenReturn(tileLayout)
+        whenever(qsPanel.tileLayout).thenReturn(tileLayout)
+        whenever(qsPanel.resources).thenReturn(resources)
+        whenever(qsPanel.viewTreeObserver).thenReturn(viewTreeObserver)
+        whenever(qsHost.tiles).thenReturn(emptyList())
+        whenever(resources.configuration).thenReturn(configuration)
+
+        underTest = createUnderTest()
+        underTest.init()
+    }
+
+    @After
+    fun tearDown() {
+        disallowTestableLooperAsMainThread()
+        Dispatchers.resetMain()
+    }
+
+    @Test
+    fun configurationChange_onlySplitShadeConfigChanges_horizontalInSceneUpdated() =
+        with(kosmos) {
+            testScope.runTest {
+                clearInvocations(qsPanel)
+
+                mediaVisible.value = true
+                runCurrent()
+                isSplitShade = false
+                configuration.orientation = Configuration.ORIENTATION_LANDSCAPE
+                configuration.screenLayout = Configuration.SCREENLAYOUT_LONG_YES
+                underTest.mOnConfigurationChangedListener.onConfigurationChange(configuration)
+
+                assertThat(underTest.shouldUseHorizontalInScene()).isTrue()
+                verify(qsPanel).setColumnRowLayout(true)
+                clearInvocations(qsPanel)
+
+                isSplitShade = true
+                underTest.mOnConfigurationChangedListener.onConfigurationChange(configuration)
+
+                assertThat(underTest.shouldUseHorizontalInScene()).isFalse()
+                verify(qsPanel).setColumnRowLayout(false)
+            }
+        }
+
+    @Test
+    fun configurationChange_shouldUseHorizontalInSceneInLongDevices() =
+        with(kosmos) {
+            testScope.runTest {
+                clearInvocations(qsPanel)
+
+                mediaVisible.value = true
+                runCurrent()
+                isSplitShade = false
+                // When device is rotated to landscape and is long
+                configuration.orientation = Configuration.ORIENTATION_LANDSCAPE
+                configuration.screenLayout = Configuration.SCREENLAYOUT_LONG_YES
+                underTest.mOnConfigurationChangedListener.onConfigurationChange(configuration)
+
+                // Then the layout changes
+                assertThat(underTest.shouldUseHorizontalInScene()).isTrue()
+                verify(qsPanel).setColumnRowLayout(true)
+                clearInvocations(qsPanel)
+
+                // When device changes to not-long
+                configuration.screenLayout = Configuration.SCREENLAYOUT_LONG_NO
+                underTest.mOnConfigurationChangedListener.onConfigurationChange(configuration)
+
+                // Then the layout changes back
+                assertThat(underTest.shouldUseHorizontalInScene()).isFalse()
+                verify(qsPanel).setColumnRowLayout(false)
+            }
+        }
+
+    @Test
+    fun configurationChange_horizontalInScene_onlyInLandscape() =
+        with(kosmos) {
+            testScope.runTest {
+                clearInvocations(qsPanel)
+
+                mediaVisible.value = true
+                runCurrent()
+                isSplitShade = false
+
+                // When device is rotated to landscape and is long
+                configuration.orientation = Configuration.ORIENTATION_LANDSCAPE
+                configuration.screenLayout = Configuration.SCREENLAYOUT_LONG_YES
+                underTest.mOnConfigurationChangedListener.onConfigurationChange(configuration)
+
+                // Then the layout changes
+                assertThat(underTest.shouldUseHorizontalInScene()).isTrue()
+                verify(qsPanel).setColumnRowLayout(true)
+                clearInvocations(qsPanel)
+
+                // When it is rotated back to portrait
+                configuration.orientation = Configuration.ORIENTATION_PORTRAIT
+                underTest.mOnConfigurationChangedListener.onConfigurationChange(configuration)
+
+                // Then the layout changes back
+                assertThat(underTest.shouldUseHorizontalInScene()).isFalse()
+                verify(qsPanel).setColumnRowLayout(false)
+            }
+        }
+
+    @Test
+    fun changeMediaVisible_changesHorizontalInScene() =
+        with(kosmos) {
+            testScope.runTest {
+                mediaVisible.value = false
+                runCurrent()
+                isSplitShade = false
+                configuration.orientation = Configuration.ORIENTATION_LANDSCAPE
+                configuration.screenLayout = Configuration.SCREENLAYOUT_LONG_YES
+                underTest.mOnConfigurationChangedListener.onConfigurationChange(configuration)
+
+                assertThat(underTest.shouldUseHorizontalInScene()).isFalse()
+                clearInvocations(qsPanel)
+
+                mediaVisible.value = true
+                runCurrent()
+
+                assertThat(underTest.shouldUseHorizontalInScene()).isTrue()
+                verify(qsPanel).setColumnRowLayout(true)
+            }
+        }
+
+    @Test
+    fun startFromMediaHorizontalLong_shouldUseHorizontal() =
+        with(kosmos) {
+            testScope.runTest {
+                mediaVisible.value = true
+                runCurrent()
+                isSplitShade = false
+                configuration.orientation = Configuration.ORIENTATION_LANDSCAPE
+                configuration.screenLayout = Configuration.SCREENLAYOUT_LONG_YES
+
+                underTest = createUnderTest()
+                underTest.init()
+                runCurrent()
+
+                assertThat(underTest.shouldUseHorizontalInScene()).isTrue()
+                verify(qsPanel).setColumnRowLayout(true)
+            }
+        }
+
+    private fun createUnderTest(): TestableQSPanelControllerBase {
+        return TestableQSPanelControllerBase(
+            qsPanel,
+            qsHost,
+            qsCustomizerController,
+            mediaHost,
+            metricsLogger,
+            uiEventLogger,
+            qsLogger,
+            dumpManager,
+            splitShadeStateController,
+            longPressEffectProvider,
+            mediaVisible,
+        )
+    }
+
+    private class TestableQSPanelControllerBase(
+        view: QSPanel,
+        qsHost: QSHost,
+        qsCustomizerController: QSCustomizerController,
+        mediaHost: MediaHost,
+        metricsLogger: MetricsLogger,
+        uiEventLogger: UiEventLogger,
+        qsLogger: QSLogger,
+        dumpManager: DumpManager,
+        splitShadeStateController: SplitShadeStateController,
+        longPressEffectProvider: Provider<QSLongPressEffect>,
+        private val mediaVisibleFlow: StateFlow<Boolean>
+    ) :
+        QSPanelControllerBase<QSPanel>(
+            view,
+            qsHost,
+            qsCustomizerController,
+            /* usingMediaPlayer= */ false,
+            mediaHost,
+            metricsLogger,
+            uiEventLogger,
+            qsLogger,
+            dumpManager,
+            splitShadeStateController,
+            longPressEffectProvider
+        ) {
+
+        init {
+            whenever(view.dumpableTag).thenReturn(hashCode().toString())
+        }
+        override fun getMediaVisibleFlow(): StateFlow<Boolean> {
+            return mediaVisibleFlow
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 542bfaa..225adab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -17,9 +17,13 @@
 package com.android.systemui.qs;
 
 import static com.android.systemui.Flags.FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS;
+import static com.android.systemui.flags.SceneContainerFlagParameterizationKt.parameterizeSceneContainerFlag;
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static kotlinx.coroutines.flow.FlowKt.asStateFlow;
+import static kotlinx.coroutines.flow.StateFlowKt.MutableStateFlow;
+
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -37,9 +41,10 @@
 import android.content.res.Resources;
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
+import android.platform.test.flag.junit.FlagsParameterization;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.ContextThemeWrapper;
+import android.view.ViewTreeObserver;
 
 import androidx.test.filters.SmallTest;
 
@@ -49,18 +54,26 @@
 import com.android.internal.logging.testing.UiEventLoggerFake;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.DisableSceneContainer;
 import com.android.systemui.haptics.qs.QSLongPressEffect;
 import com.android.systemui.kosmos.KosmosJavaAdapter;
+import com.android.systemui.lifecycle.InstantTaskExecutorRule;
 import com.android.systemui.media.controls.ui.view.MediaHost;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.customize.QSCustomizerController;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.res.R;
+import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
 import com.android.systemui.util.animation.DisappearParameters;
 
+import kotlinx.coroutines.flow.MutableStateFlow;
+import kotlinx.coroutines.flow.StateFlow;
+
+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;
@@ -73,11 +86,22 @@
 
 import javax.inject.Provider;
 
-@RunWith(AndroidTestingRunner.class)
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
+@RunWith(ParameterizedAndroidJunit4.class)
 @RunWithLooper
 @SmallTest
 public class QSPanelControllerBaseTest extends SysuiTestCase {
 
+    @Rule
+    public final InstantTaskExecutorRule mInstantTaskExecutor = new InstantTaskExecutorRule();
+
+    @Parameters(name = "{0}")
+    public static List<FlagsParameterization> getParams() {
+        return parameterizeSceneContainerFlag();
+    }
+
     private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
     @Mock
     private QSPanel mQSPanel;
@@ -109,10 +133,13 @@
     Configuration mConfiguration;
     @Mock
     Runnable mHorizontalLayoutListener;
+    @Mock
+    private ViewTreeObserver mViewTreeObserver;
+
     private TestableLongPressEffectProvider mLongPressEffectProvider =
             new TestableLongPressEffectProvider();
 
-    private QSPanelControllerBase<QSPanel> mController;
+    private TestableQSPanelControllerBase mController;
 
     /** Implementation needed to ensure we have a reflectively-available class name. */
     private class TestableQSPanelControllerBase extends QSPanelControllerBase<QSPanel> {
@@ -120,15 +147,27 @@
                 QSCustomizerController qsCustomizerController, MediaHost mediaHost,
                 MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
                 DumpManager dumpManager) {
-            super(view, host, qsCustomizerController, true, mediaHost, metricsLogger, uiEventLogger,
+            super(view, host, qsCustomizerController, usingMediaPlayer(),
+                    mediaHost, metricsLogger, uiEventLogger,
                     qsLogger, dumpManager, new ResourcesSplitShadeStateController(),
                     mLongPressEffectProvider);
         }
 
+        private MutableStateFlow<Boolean> mMediaVisible = MutableStateFlow(false);
+
         @Override
         protected QSTileRevealController createTileRevealController() {
             return mQSTileRevealController;
         }
+
+        @Override
+        StateFlow<Boolean> getMediaVisibleFlow() {
+            return asStateFlow(mMediaVisible);
+        }
+
+        void setMediaVisible(boolean visible) {
+            mMediaVisible.tryEmit(visible);
+        }
     }
 
     private class TestableLongPressEffectProvider implements Provider<QSLongPressEffect> {
@@ -142,16 +181,24 @@
         }
     }
 
+    public QSPanelControllerBaseTest(FlagsParameterization flags) {
+        super();
+        mSetFlagsRule.setFlagsParameterization(flags);
+    }
+
     @Before
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
 
+        allowTestableLooperAsMainThread();
+
         when(mQSPanel.isAttachedToWindow()).thenReturn(true);
         when(mQSPanel.getDumpableTag()).thenReturn("QSPanel");
         when(mQSPanel.openPanelEvent()).thenReturn(QSEvent.QS_PANEL_EXPANDED);
         when(mQSPanel.closePanelEvent()).thenReturn(QSEvent.QS_PANEL_COLLAPSED);
         when(mQSPanel.getOrCreateTileLayout()).thenReturn(mPagedTileLayout);
         when(mQSPanel.getTileLayout()).thenReturn(mPagedTileLayout);
+        when(mQSPanel.getViewTreeObserver()).thenReturn(mViewTreeObserver);
         when(mQSTile.getTileSpec()).thenReturn("dnd");
         when(mQSHost.getTiles()).thenReturn(Collections.singleton(mQSTile));
         when(mQSTileRevealControllerFactory.create(any(), any()))
@@ -174,6 +221,11 @@
         reset(mQSTileRevealController);
     }
 
+    @After
+    public void tearDown() {
+        disallowTestableLooperAsMainThread();
+    }
+
     @Test
     public void testSetRevealExpansion_preAttach() {
         mController.onViewDetached();
@@ -269,6 +321,7 @@
 
 
     @Test
+    @DisableSceneContainer
     public void testShouldUseHorizontalLayout_falseForSplitShade() {
         mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
         mConfiguration.screenLayout = Configuration.SCREENLAYOUT_LONG_YES;
@@ -294,6 +347,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     public void testChangeConfiguration_shouldUseHorizontalLayoutInLandscape_true() {
         when(mMediaHost.getVisible()).thenReturn(true);
         mController.setUsingHorizontalLayoutChangeListener(mHorizontalLayoutListener);
@@ -317,6 +371,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     public void testChangeConfiguration_shouldUseHorizontalLayoutInLongDevices_true() {
         when(mMediaHost.getVisible()).thenReturn(true);
         mController.setUsingHorizontalLayoutChangeListener(mHorizontalLayoutListener);
@@ -353,6 +408,7 @@
     }
 
     @Test
+    @DisableSceneContainer
     public void configurationChange_onlySplitShadeConfigChanges_horizontalLayoutStatusUpdated() {
         // Preconditions for horizontal layout
         when(mMediaHost.getVisible()).thenReturn(true);
@@ -502,4 +558,20 @@
         verify(mQSPanel, times(2)).removeTile(any());
         verify(mQSPanel, times(2)).addTile(any());
     }
+
+    @Test
+    public void dettach_destroy_attach_tilesAreNotReadded() {
+        when(mQSHost.getTiles()).thenReturn(List.of(mQSTile, mOtherTile));
+        mController.setTiles();
+
+        mController.onViewDetached();
+        mController.destroy();
+        mController.onViewAttached();
+
+        assertThat(mController.mRecords).isEmpty();
+    }
+
+    private boolean usingMediaPlayer() {
+        return !SceneContainerFlag.isEnabled();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
index e50320d..02c5b5a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -1,8 +1,8 @@
 package com.android.systemui.qs
 
 import android.content.res.Configuration
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import android.testing.AndroidTestingRunner
 import android.testing.TestableResources
 import android.view.ContextThemeWrapper
 import com.android.internal.logging.MetricsLogger
@@ -10,6 +10,7 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.haptics.qs.QSLongPressEffect
+import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
 import com.android.systemui.media.controls.ui.view.MediaHost
 import com.android.systemui.media.controls.ui.view.MediaHostState
 import com.android.systemui.plugins.FalsingManager
@@ -17,6 +18,7 @@
 import com.android.systemui.qs.customize.QSCustomizerController
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
 import com.android.systemui.settings.brightness.BrightnessController
 import com.android.systemui.settings.brightness.BrightnessSliderController
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
@@ -39,7 +41,7 @@
 import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class QSPanelControllerTest : SysuiTestCase() {
 
     @Mock private lateinit var qsPanel: QSPanel
@@ -63,6 +65,9 @@
     @Mock private lateinit var configuration: Configuration
     @Mock private lateinit var pagedTileLayout: PagedTileLayout
     @Mock private lateinit var longPressEffectProvider: Provider<QSLongPressEffect>
+    @Mock private lateinit var mediaCarouselInteractor: MediaCarouselInteractor
+
+    private val usingMediaPlayer: Boolean by lazy { !SceneContainerFlag.isEnabled }
 
     private lateinit var controller: QSPanelController
     private val testableResources: TestableResources = mContext.orCreateTestableResources
@@ -88,7 +93,7 @@
             tunerService,
             qsHost,
             qsCustomizerController,
-            /* usingMediaPlayer= */ true,
+            /* usingMediaPlayer= */ usingMediaPlayer,
             mediaHost,
             qsTileRevealControllerFactory,
             dumpManager,
@@ -101,6 +106,7 @@
             statusBarKeyguardViewManager,
             ResourcesSplitShadeStateController(),
             longPressEffectProvider,
+            mediaCarouselInteractor,
         )
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelSwitchToParentTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelSwitchToParentTest.kt
index 56f2905..56e25fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelSwitchToParentTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelSwitchToParentTest.kt
@@ -18,9 +18,9 @@
 
 import com.google.common.truth.Truth.assertThat
 
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 
-import android.testing.AndroidTestingRunner
 import android.view.View
 import android.view.ViewGroup
 import android.widget.FrameLayout
@@ -30,7 +30,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class QSPanelSwitchToParentTest : SysuiTestCase() {
 
@@ -159,4 +159,4 @@
 
     private val ViewGroup.childrenList: List<View>
         get() = children.toList()
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
index 5c6ed70..2d282dc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -14,7 +14,6 @@
 package com.android.systemui.qs
 
 import android.graphics.Rect
-import android.testing.AndroidTestingRunner
 import android.testing.TestableContext
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
@@ -26,6 +25,7 @@
 import android.view.accessibility.AccessibilityNodeInfo
 import android.widget.FrameLayout
 import android.widget.LinearLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
@@ -45,7 +45,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @RunWithLooper
 @SmallTest
 class QSPanelTest : SysuiTestCase() {
@@ -275,6 +275,19 @@
         ViewUtils.detachView(panel)
     }
 
+    @Test
+    fun setRowColumnLayout() {
+        qsPanel.setColumnRowLayout(/* withMedia= */ false)
+
+        assertThat(qsPanel.tileLayout!!.minRows).isEqualTo(1)
+        assertThat(qsPanel.tileLayout!!.maxColumns).isEqualTo(4)
+
+        qsPanel.setColumnRowLayout(/* withMedia= */ true)
+
+        assertThat(qsPanel.tileLayout!!.minRows).isEqualTo(2)
+        assertThat(qsPanel.tileLayout!!.maxColumns).isEqualTo(2)
+    }
+
     private infix fun View.isLeftOf(other: View): Boolean {
         val rect = Rect()
         getBoundsOnScreen(rect)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index 0abcc64..dad65f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -42,7 +42,6 @@
 import android.os.Looper;
 import android.provider.DeviceConfig;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.text.SpannableStringBuilder;
@@ -51,6 +50,7 @@
 import android.widget.TextView;
 
 import androidx.annotation.Nullable;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -83,7 +83,7 @@
 */
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RunWithLooper
 public class QSSecurityFooterTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
index e2a0626..ecdabbf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSquishinessControllerTest.kt
@@ -1,6 +1,6 @@
 package com.android.systemui.qs
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import org.junit.Before
@@ -12,7 +12,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.junit.MockitoJUnit
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class QSSquishinessControllerTest : SysuiTestCase() {
 
@@ -45,4 +45,4 @@
         verify(qsPanelController).setSquishinessFraction(0.5f)
         verify(quickQsPanelController).setSquishinessFraction(0.5f)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 5e14b1a..6d1bc82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -41,10 +41,10 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
 import android.util.SparseArray;
 
 import androidx.annotation.Nullable;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.MetricsLogger;
@@ -53,7 +53,6 @@
 import com.android.systemui.animation.Expandable;
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.dump.nano.SystemUIProtoDump;
-import com.android.systemui.flags.FakeFeatureFlags;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.PluginManager;
 import com.android.systemui.plugins.qs.QSFactory;
@@ -95,7 +94,7 @@
 
 import javax.inject.Provider;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class QSTileHostTest extends SysuiTestCase {
 
@@ -133,8 +132,6 @@
 
     private SparseArray<SharedPreferences> mSharedPreferencesByUser;
 
-    private FakeFeatureFlags mFeatureFlags;
-
     private QSPipelineFlagsRepository mQSPipelineFlagsRepository;
 
     private FakeExecutor mMainExecutor;
@@ -144,7 +141,6 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
-        mFeatureFlags = new FakeFeatureFlags();
 
         mSetFlagsRule.disableFlags(FLAG_QS_NEW_PIPELINE);
         mSetFlagsRule.disableFlags(FLAG_QS_NEW_TILES);
@@ -170,12 +166,12 @@
         saveSetting("");
         setUpTileFactory();
         mQSTileHost = new TestQSTileHost(mContext, () -> null, mDefaultFactory, mMainExecutor,
-                mPluginManager, mTunerService, () -> mAutoTiles, mShadeController,
+                mPluginManager, mTunerService, () -> mAutoTiles, () -> mShadeController,
                 mQSLogger, mUserTracker, mSecureSettings, mCustomTileStatePersister,
                 mTileLifecycleManagerFactory, mUserFileManager, mQSPipelineFlagsRepository);
         mMainExecutor.runAllReady();
 
-        mSecureSettings.registerContentObserverForUser(SETTING, new ContentObserver(null) {
+        mSecureSettings.registerContentObserverForUserSync(SETTING, new ContentObserver(null) {
             @Override
             public void onChange(boolean selfChange) {
                 super.onChange(selfChange);
@@ -689,7 +685,7 @@
                 QSFactory defaultFactory, Executor mainExecutor,
                 PluginManager pluginManager, TunerService tunerService,
                 Provider<AutoTileManager> autoTiles,
-                ShadeController shadeController, QSLogger qsLogger,
+                Lazy<ShadeController> shadeController, QSLogger qsLogger,
                 UserTracker userTracker, SecureSettings secureSettings,
                 CustomTileStatePersister customTileStatePersister,
                 TileLifecycleManager.Factory tileLifecycleManagerFactory,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 1eb0a51..369bb22 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -17,14 +17,15 @@
 package com.android.systemui.qs
 
 import android.content.res.Configuration
-import android.testing.AndroidTestingRunner
 import android.view.ContextThemeWrapper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.haptics.qs.QSLongPressEffect
+import com.android.systemui.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
 import com.android.systemui.media.controls.ui.view.MediaHost
 import com.android.systemui.media.controls.ui.view.MediaHostState
 import com.android.systemui.plugins.qs.QSTile
@@ -49,7 +50,7 @@
 import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class QuickQSPanelControllerTest : SysuiTestCase() {
 
     @Mock private lateinit var quickQSPanel: QuickQSPanel
@@ -62,6 +63,10 @@
     @Mock private lateinit var tileLayout: TileLayout
     @Captor private lateinit var captor: ArgumentCaptor<QSPanel.OnConfigurationChangedListener>
     @Mock private lateinit var longPressEffectProvider: Provider<QSLongPressEffect>
+    @Mock private lateinit var mediaCarouselInteractor: MediaCarouselInteractor
+
+    private val usingMediaPlayer: Boolean
+        get() = false
 
     private val uiEventLogger = UiEventLoggerFake()
     private val dumpManager = DumpManager()
@@ -86,7 +91,7 @@
                 quickQSPanel,
                 qsHost,
                 qsCustomizerController,
-                /* usingMediaPlayer = */ false,
+                usingMediaPlayer,
                 mediaHost,
                 { usingCollapsedLandscapeMedia },
                 metricsLogger,
@@ -94,6 +99,7 @@
                 qsLogger,
                 dumpManager,
                 longPressEffectProvider,
+                mediaCarouselInteractor,
             )
 
         controller.init()
@@ -163,6 +169,7 @@
         qsLogger: QSLogger,
         dumpManager: DumpManager,
         longPressEffectProvider: Provider<QSLongPressEffect>,
+        mediaCarouselInteractor: MediaCarouselInteractor,
     ) :
         QuickQSPanelController(
             view,
@@ -177,6 +184,7 @@
             dumpManager,
             ResourcesSplitShadeStateController(),
             longPressEffectProvider,
+            mediaCarouselInteractor
         ) {
 
         private var rotation = RotationUtils.ROTATION_NONE
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelTest.kt
index e5369fc..3d6ba94 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelTest.kt
@@ -1,10 +1,10 @@
 package com.android.systemui.qs
 
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.ViewGroup
 import android.view.accessibility.AccessibilityNodeInfo
 import android.widget.FrameLayout
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.qs.logging.QSLogger
@@ -16,7 +16,7 @@
 import org.mockito.Mockito
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 @SmallTest
 class QuickQSPanelTest : SysuiTestCase() {
@@ -63,4 +63,4 @@
         quickQSPanel.performAccessibilityAction(AccessibilityNodeInfo.ACTION_EXPAND, null)
         Mockito.verify(mockRunnable).run()
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
index 4915e55..a0ccec1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.qs
 
 import android.content.Context
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import org.junit.After
@@ -31,7 +31,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class QuickStatusBarHeaderControllerTest : SysuiTestCase() {
 
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TileStateToProtoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/TileStateToProtoTest.kt
index bc947fb..be388a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/TileStateToProtoTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TileStateToProtoTest.kt
@@ -2,8 +2,8 @@
 
 import android.content.ComponentName
 import android.service.quicksettings.Tile
-import android.testing.AndroidTestingRunner
 import android.widget.Switch
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.plugins.qs.QSTile
@@ -12,7 +12,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class TileStateToProtoTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/UserSettingObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/UserSettingObserverTest.kt
index 8f06fe2..90e0dd8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/UserSettingObserverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/UserSettingObserverTest.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.qs
 
 import android.os.Handler
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.settings.FakeSettings
@@ -33,7 +33,7 @@
 private typealias Callback = (Int, Boolean) -> Unit
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class UserSettingObserverTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java
index 6e2f5db..9f2b1ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterDelegateTest.java
@@ -24,11 +24,11 @@
 import static org.mockito.Mockito.when;
 
 import android.os.Bundle;
-import android.testing.AndroidTestingRunner;
 import android.view.View;
 import android.view.accessibility.AccessibilityNodeInfo;
 
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -41,7 +41,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class TileAdapterDelegateTest extends SysuiTestCase {
 
     private static final int MOVE_TO_POSITION_ID = R.id.accessibility_action_qs_move_to_position;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java
index f8a98af..cbcd810 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileAdapterTest.java
@@ -17,10 +17,10 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.verify;
 
-import android.testing.AndroidTestingRunner;
 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.testing.UiEventLoggerFake;
@@ -37,7 +37,7 @@
 
 import java.util.Collections;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RunWithLooper
 @SmallTest
 public class TileAdapterTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index 8bf743884..09a6c2c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -40,12 +40,12 @@
 import android.content.pm.ServiceInfo;
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.text.TextUtils;
 import android.util.ArraySet;
 
 import androidx.annotation.Nullable;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.InstanceId;
@@ -75,7 +75,7 @@
 import java.util.concurrent.Executor;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class TileQueryHelperTest extends SysuiTestCase {
     private static final String CURRENT_TILES = "internet,dnd,nfc";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt
index 81d02b8..14eaa03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileStatePersisterTest.kt
@@ -20,7 +20,7 @@
 import android.content.Context
 import android.content.SharedPreferences
 import android.service.quicksettings.Tile
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.util.mockito.capture
@@ -41,7 +41,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class CustomTileStatePersisterTest : SysuiTestCase() {
 
     companion object {
@@ -167,4 +167,4 @@
 
         assertThat(customTileStatePersister.readState(KEY)!!.label).isNull()
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index a8e9db5..bd03acb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -31,8 +31,8 @@
 import android.os.Parcel
 import android.service.quicksettings.IQSTileService
 import android.service.quicksettings.Tile
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.IWindowManager
 import com.android.internal.logging.MetricsLogger
@@ -74,7 +74,7 @@
 
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class CustomTileTest : SysuiTestCase() {
 
@@ -563,4 +563,4 @@
     parcel.setDataPosition(0)
 
     return Tile.CREATOR.createFromParcel(parcel)
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
index 78c2acf..2db5e83 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileRequestDialogTest.kt
@@ -22,11 +22,11 @@
 import android.graphics.Color
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.Icon
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.ViewGroup
 import android.widget.ImageView
 import android.widget.TextView
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
@@ -48,7 +48,7 @@
 import java.util.Arrays
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class TileRequestDialogTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt
index 3afa6ad..89ec687 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceRequestControllerTest.kt
@@ -22,7 +22,7 @@
 import android.content.DialogInterface
 import android.graphics.drawable.Icon
 import android.os.RemoteException
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.InstanceId
 import com.android.internal.statusbar.IAddTileResultCallback
@@ -51,7 +51,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class TileServiceRequestControllerTest : SysuiTestCase() {
 
     companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
index 720c25a..c5a2370 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractorTest.kt
@@ -19,8 +19,8 @@
 import android.content.Context
 import android.content.Intent
 import android.provider.Settings
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.nano.MetricsProto
 import com.android.internal.logging.testing.FakeMetricsLogger
@@ -49,7 +49,7 @@
 import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class FooterActionsInteractorTest : SysuiTestCase() {
     private lateinit var utils: FooterActionsTestUtils
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
index 1cb3bf6..31652a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
@@ -18,10 +18,10 @@
 
 import android.graphics.drawable.Drawable
 import android.os.UserManager
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.testing.TestableLooper.RunWithLooper
 import android.view.ContextThemeWrapper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.settingslib.Utils
 import com.android.settingslib.drawable.UserIconDrawable
@@ -57,7 +57,7 @@
 import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @RunWithLooper
 class FooterActionsViewModelTest : SysuiTestCase() {
     private val testScope = TestScope()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/data/QSPreferencesRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/data/QSPreferencesRepositoryTest.kt
new file mode 100644
index 0000000..b0aa6dd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/data/QSPreferencesRepositoryTest.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.data
+
+import android.content.Context
+import android.content.SharedPreferences
+import android.content.pm.UserInfo
+import android.testing.AndroidTestingRunner
+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.panels.data.repository.QSPreferencesRepository
+import com.android.systemui.qs.panels.data.repository.qsPreferencesRepository
+import com.android.systemui.settings.userFileManager
+import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.user.data.repository.userRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class QSPreferencesRepositoryTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val underTest = with(kosmos) { qsPreferencesRepository }
+
+    @Test
+    fun showLabels_updatesFromSharedPreferences() =
+        with(kosmos) {
+            testScope.runTest {
+                val latest by collectLastValue(underTest.showLabels)
+                assertThat(latest).isFalse()
+
+                setShowLabelsInSharedPreferences(true)
+                assertThat(latest).isTrue()
+
+                setShowLabelsInSharedPreferences(false)
+                assertThat(latest).isFalse()
+            }
+        }
+
+    @Test
+    fun showLabels_updatesFromUserChange() =
+        with(kosmos) {
+            testScope.runTest {
+                fakeUserRepository.setUserInfos(USERS)
+                val latest by collectLastValue(underTest.showLabels)
+
+                fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
+                setShowLabelsInSharedPreferences(false)
+
+                fakeUserRepository.setSelectedUserInfo(ANOTHER_USER)
+                setShowLabelsInSharedPreferences(true)
+
+                fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
+                assertThat(latest).isFalse()
+            }
+        }
+
+    @Test
+    fun setShowLabels_inSharedPreferences() {
+        underTest.setShowLabels(false)
+        assertThat(getShowLabelsFromSharedPreferences(true)).isFalse()
+
+        underTest.setShowLabels(true)
+        assertThat(getShowLabelsFromSharedPreferences(false)).isTrue()
+    }
+
+    @Test
+    fun setShowLabels_forDifferentUser() =
+        with(kosmos) {
+            testScope.runTest {
+                fakeUserRepository.setUserInfos(USERS)
+
+                fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
+                underTest.setShowLabels(false)
+                assertThat(getShowLabelsFromSharedPreferences(true)).isFalse()
+
+                fakeUserRepository.setSelectedUserInfo(ANOTHER_USER)
+                underTest.setShowLabels(true)
+                assertThat(getShowLabelsFromSharedPreferences(false)).isTrue()
+
+                fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
+                assertThat(getShowLabelsFromSharedPreferences(true)).isFalse()
+            }
+        }
+
+    private fun getSharedPreferences(): SharedPreferences =
+        with(kosmos) {
+            return userFileManager.getSharedPreferences(
+                QSPreferencesRepository.FILE_NAME,
+                Context.MODE_PRIVATE,
+                userRepository.getSelectedUserInfo().id,
+            )
+        }
+
+    private fun setShowLabelsInSharedPreferences(value: Boolean) {
+        getSharedPreferences().edit().putBoolean(ICON_LABELS_KEY, value).apply()
+    }
+
+    private fun getShowLabelsFromSharedPreferences(defaultValue: Boolean): Boolean {
+        return getSharedPreferences().getBoolean(ICON_LABELS_KEY, defaultValue)
+    }
+
+    companion object {
+        private const val ICON_LABELS_KEY = "show_icon_labels"
+        private const val PRIMARY_USER_ID = 0
+        private val PRIMARY_USER = UserInfo(PRIMARY_USER_ID, "user 0", UserInfo.FLAG_MAIN)
+        private const val ANOTHER_USER_ID = 1
+        private val ANOTHER_USER = UserInfo(ANOTHER_USER_ID, "user 1", UserInfo.FLAG_FULL)
+        private val USERS = listOf(PRIMARY_USER, ANOTHER_USER)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt
index 2da4b72..b206f56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.qs.panels.domain.interactor
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.kosmos.testScope
@@ -31,9 +31,6 @@
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
@@ -42,26 +39,25 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class GridConsistencyInteractorTest : SysuiTestCase() {
 
     private val iconOnlyTiles =
-        MutableStateFlow(
-            setOf(
-                TileSpec.create("smallA"),
-                TileSpec.create("smallB"),
-                TileSpec.create("smallC"),
-                TileSpec.create("smallD"),
-                TileSpec.create("smallE"),
-            )
+        setOf(
+            TileSpec.create("smallA"),
+            TileSpec.create("smallB"),
+            TileSpec.create("smallC"),
+            TileSpec.create("smallD"),
+            TileSpec.create("smallE"),
         )
 
     private val kosmos =
         testKosmos().apply {
             iconTilesRepository =
                 object : IconTilesRepository {
-                    override val iconTilesSpecs: StateFlow<Set<TileSpec>>
-                        get() = iconOnlyTiles.asStateFlow()
+                    override fun isIconTile(spec: TileSpec): Boolean {
+                        return iconOnlyTiles.contains(spec)
+                    }
                 }
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorTest.kt
new file mode 100644
index 0000000..9b08432e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorTest.kt
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.domain.interactor
+
+import android.content.pm.UserInfo
+import android.testing.AndroidTestingRunner
+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.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class IconLabelVisibilityInteractorTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val underTest = with(kosmos) { iconLabelVisibilityInteractor }
+
+    @Before
+    fun setUp() {
+        with(kosmos) { fakeUserRepository.setUserInfos(USERS) }
+    }
+
+    @Test
+    fun changingShowLabels_receivesCorrectShowLabels() =
+        with(kosmos) {
+            testScope.runTest {
+                val showLabels by collectLastValue(underTest.showLabels)
+
+                underTest.setShowLabels(false)
+                runCurrent()
+                assertThat(showLabels).isFalse()
+
+                underTest.setShowLabels(true)
+                runCurrent()
+                assertThat(showLabels).isTrue()
+            }
+        }
+
+    @Test
+    fun changingUser_receivesCorrectShowLabels() =
+        with(kosmos) {
+            testScope.runTest {
+                val showLabels by collectLastValue(underTest.showLabels)
+
+                fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
+                underTest.setShowLabels(false)
+                runCurrent()
+                assertThat(showLabels).isFalse()
+
+                fakeUserRepository.setSelectedUserInfo(ANOTHER_USER)
+                underTest.setShowLabels(true)
+                runCurrent()
+                assertThat(showLabels).isTrue()
+
+                fakeUserRepository.setSelectedUserInfo(PRIMARY_USER)
+                runCurrent()
+                assertThat(showLabels).isFalse()
+            }
+        }
+
+    companion object {
+        private val PRIMARY_USER = UserInfo(0, "user 0", UserInfo.FLAG_MAIN)
+        private val ANOTHER_USER = UserInfo(1, "user 1", UserInfo.FLAG_FULL)
+        private val USERS = listOf(PRIMARY_USER, ANOTHER_USER)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorTest.kt
index bda48ad..1e2e82f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.qs.panels.domain.interactor
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.kosmos.testScope
@@ -25,33 +25,29 @@
 import com.android.systemui.qs.pipeline.shared.TileSpec
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class InfiniteGridConsistencyInteractorTest : SysuiTestCase() {
 
     private val iconOnlyTiles =
-        MutableStateFlow(
-            setOf(
-                TileSpec.create("smallA"),
-                TileSpec.create("smallB"),
-                TileSpec.create("smallC"),
-                TileSpec.create("smallD"),
-                TileSpec.create("smallE"),
-            )
+        setOf(
+            TileSpec.create("smallA"),
+            TileSpec.create("smallB"),
+            TileSpec.create("smallC"),
+            TileSpec.create("smallD"),
+            TileSpec.create("smallE"),
         )
     private val kosmos =
         testKosmos().apply {
             iconTilesRepository =
                 object : IconTilesRepository {
-                    override val iconTilesSpecs: StateFlow<Set<TileSpec>>
-                        get() = iconOnlyTiles.asStateFlow()
+                    override fun isIconTile(spec: TileSpec): Boolean {
+                        return iconOnlyTiles.contains(spec)
+                    }
                 }
         }
     private val underTest = with(kosmos) { infiniteGridConsistencyInteractor }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
index 5201e5d..12c566c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSFactoryImplTest.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.qs.tileimpl
 
 import android.content.ComponentName
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.qs.QSHost
@@ -93,7 +93,7 @@
         "font_scaling" to FontScalingTile::class.java
 )
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class QSFactoryImplTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
index 81a9604..2580ac2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
@@ -27,10 +27,10 @@
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
 import android.service.quicksettings.Tile;
-import android.testing.AndroidTestingRunner;
 import android.testing.UiThreadTest;
 import android.widget.ImageView;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -43,7 +43,7 @@
 import org.mockito.InOrder;
 import org.mockito.Mockito;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @UiThreadTest
 @SmallTest
 public class QSIconViewImplTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index c706244..e46416c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -48,11 +48,11 @@
 import android.os.Looper;
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
 import androidx.annotation.Nullable;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.InstanceId;
@@ -85,7 +85,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class QSTileImplTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
index db11c3e..6618308 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
@@ -19,20 +19,24 @@
 import android.content.Context
 import android.graphics.Rect
 import android.graphics.drawable.Drawable
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.service.quicksettings.Tile
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.text.TextUtils
 import android.view.ContextThemeWrapper
 import android.view.View
 import android.view.accessibility.AccessibilityNodeInfo
 import android.widget.TextView
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS
 import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.haptics.qs.QSLongPressEffect
 import com.android.systemui.haptics.qs.qsLongPressEffect
 import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.qsTileFactory
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -41,7 +45,7 @@
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class QSTileViewImplTest : SysuiTestCase() {
@@ -536,10 +540,30 @@
         assertThat(tileView.haveLongPressPropertiesBeenReset).isTrue()
     }
 
+    @Test
+    @EnableFlags(FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS)
+    fun onInit_withLongPressEffect_longPressEffectHasTileAndExpandable() {
+        val tile = kosmos.qsTileFactory.createTile("Test Tile")
+        tileView.init(tile)
+
+        assertThat(tileView.isTileAddedToLongPress).isTrue()
+        assertThat(tileView.isExpandableAddedToLongPress).isTrue()
+    }
+
+    @Test
+    @DisableFlags(FLAG_QUICK_SETTINGS_VISUAL_HAPTICS_LONGPRESS)
+    fun onInit_withoutLongPressEffect_longPressEffectDoesNotHaveTileAndExpandable() {
+        val tile = kosmos.qsTileFactory.createTile("Test Tile")
+        tileView.init(tile)
+
+        assertThat(tileView.isTileAddedToLongPress).isFalse()
+        assertThat(tileView.isExpandableAddedToLongPress).isFalse()
+    }
+
     class FakeTileView(
         context: Context,
         collapsed: Boolean,
-        longPressEffect: QSLongPressEffect?,
+        private val longPressEffect: QSLongPressEffect?,
     ) : QSTileViewImpl(
             ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings),
             collapsed,
@@ -547,6 +571,11 @@
     ) {
         var constantLongPressEffectDuration = 500
 
+        val isTileAddedToLongPress: Boolean
+            get() = longPressEffect?.qsTile != null
+        val isExpandableAddedToLongPress: Boolean
+            get() = longPressEffect?.expandable != null
+
         override fun getLongPressEffectDuration(): Int = constantLongPressEffectDuration
         fun changeState(state: QSTile.State) {
             handleStateChanged(state)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/ResourceIconTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/ResourceIconTest.kt
index 9c1cad6..e112bb0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/ResourceIconTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/ResourceIconTest.kt
@@ -16,14 +16,14 @@
 
 package com.android.systemui.qs.tileimpl
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import org.junit.Assert.assertEquals
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class ResourceIconTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt
index f2400ec..9c20a6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/TilesStatesTextTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.qs.tileimpl
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.MediumTest
 import com.android.systemui.res.R
 import com.android.systemui.SysuiTestCase
@@ -26,7 +26,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @MediumTest
 class TilesStatesTextTest : SysuiTestCase() {
 
@@ -76,4 +76,4 @@
         assertThat(SubtitleArrayMapping.getSubtitleId(null))
             .isEqualTo(R.array.tile_states_default)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
index ad87315..7a99aef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt
@@ -18,8 +18,8 @@
 
 import android.net.ConnectivityManager
 import android.os.Handler
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.internal.telephony.flags.Flags
@@ -50,7 +50,7 @@
 import org.mockito.kotlin.times
 import org.mockito.kotlin.verify
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 class AirplaneModeTileTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
index 52b8455..518b6de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt
@@ -5,8 +5,8 @@
 import android.os.Handler
 import android.provider.AlarmClock
 import android.service.quicksettings.Tile
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.SysuiTestCase
@@ -33,7 +33,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class AlarmTileTest : SysuiTestCase() {
 
@@ -152,4 +152,4 @@
         verify(activityStarter).postStartActivityDismissingKeyguard(pendingIntent,
                 null /* animationController */)
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
index 2c49e92..d6be314 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt
@@ -18,9 +18,9 @@
 
 import android.content.Context
 import android.os.Handler
-import android.testing.AndroidTestingRunner
 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.systemui.SysuiTestCase
@@ -50,7 +50,7 @@
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @RunWithLooper(setAsMainLooper = true)
 @SmallTest
 class BatterySaverTileTest : SysuiTestCase() {
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 1ffbb7b..9a924ed 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
@@ -4,9 +4,9 @@
 import android.os.Handler
 import android.os.Looper
 import android.os.UserManager
-import android.testing.AndroidTestingRunner
 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.internal.telephony.flags.Flags
@@ -42,7 +42,7 @@
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @RunWithLooper(setAsMainLooper = true)
 @SmallTest
 class BluetoothTileTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
index 0e4b113..093cdf2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt
@@ -19,8 +19,8 @@
 import android.os.Handler
 import android.provider.Settings
 import android.safetycenter.SafetyCenterManager
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.res.R
@@ -44,7 +44,7 @@
 import org.mockito.MockitoAnnotations
 import org.mockito.Mockito.`when` as whenever
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 class CameraToggleTileTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
index 46ee569..50cf5cc5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java
@@ -32,10 +32,10 @@
 import android.media.projection.MediaProjectionInfo;
 import android.os.Handler;
 import android.service.quicksettings.Tile;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
 import androidx.lifecycle.LifecycleOwner;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.MetricsLogger;
@@ -73,7 +73,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class CastTileTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java
index 2250ef3..028beb5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java
@@ -26,9 +26,9 @@
 import android.content.Intent;
 import android.os.Handler;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.MetricsLogger;
@@ -51,7 +51,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class ColorCorrectionTileTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
index ea43326..1343527 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
@@ -26,9 +26,9 @@
 import android.content.Intent;
 import android.os.Handler;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.MetricsLogger;
@@ -54,7 +54,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class ColorInversionTileTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
index 043ddf5..73ae4ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.qs.tiles
 
 import android.os.Handler
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.SysuiTestCase
@@ -43,7 +43,7 @@
 import org.mockito.Mock
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 class DataSaverTileTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index 874368b..418d126 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -22,9 +22,9 @@
 import android.os.Handler
 import android.provider.Settings
 import android.service.quicksettings.Tile
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import androidx.lifecycle.LifecycleOwner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.res.R
@@ -70,7 +70,7 @@
 import java.util.Optional
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class DeviceControlsTileTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
index 1173fa3..e01744e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
@@ -23,9 +23,9 @@
 import android.provider.Settings
 import android.provider.Settings.Global.ZEN_MODE_NO_INTERRUPTIONS
 import android.provider.Settings.Global.ZEN_MODE_OFF
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.ContextThemeWrapper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.res.R
@@ -59,7 +59,7 @@
 import org.mockito.Mockito.`when` as whenever
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class DndTileTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
index a3c2975..190d80f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java
@@ -35,9 +35,9 @@
 import android.provider.Settings;
 import android.service.dreams.IDreamManager;
 import android.service.quicksettings.Tile;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.MetricsLogger;
@@ -63,7 +63,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class DreamTileTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
index c1a0928..f90463e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
@@ -2,8 +2,8 @@
 
 import android.content.Context
 import android.os.Handler
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.res.R
@@ -26,7 +26,7 @@
 import org.mockito.Mockito
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 class FlashlightTileTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
index 1c42dd1..c854920 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
@@ -18,8 +18,8 @@
 import android.content.Intent
 import android.os.Handler
 import android.provider.Settings
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.SysuiTestCase
@@ -52,7 +52,7 @@
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 class FontScalingTileTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
index 56671bf..59ee0b8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
@@ -28,10 +28,10 @@
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.View;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.MetricsLogger;
@@ -57,7 +57,7 @@
 import org.mockito.junit.MockitoRule;
 
 /** Tests for {@link HearingDevicesTile}. */
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class HearingDevicesTileTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java
index a85b49b6..5bd6944 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java
@@ -23,9 +23,9 @@
 
 import android.os.Handler;
 import android.service.quicksettings.Tile;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
@@ -54,7 +54,7 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class HotspotTileTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
index 0ea61f9..8ea79d7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
@@ -23,9 +23,9 @@
 
 import android.os.Handler;
 import android.service.quicksettings.Tile;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.MetricsLogger;
@@ -51,7 +51,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class InternetTileTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
index 62a50e3..0a1455f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt
@@ -18,8 +18,8 @@
 
 import android.content.Context
 import android.os.Handler
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.res.R
@@ -47,7 +47,7 @@
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 class LocationTileTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
index b98a7570..dbdf3a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt
@@ -19,8 +19,8 @@
 import android.os.Handler
 import android.provider.Settings
 import android.safetycenter.SafetyCenterManager
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.res.R
@@ -44,7 +44,7 @@
 import org.mockito.MockitoAnnotations
 import org.mockito.Mockito.`when` as whenever
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 class MicrophoneToggleTileTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
index f6bc692..442a948 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
@@ -24,9 +24,9 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Handler;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.MetricsLogger;
@@ -47,7 +47,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class NfcTileTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
index a1d9e414..f1c5895 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
@@ -19,8 +19,8 @@
 import android.hardware.display.ColorDisplayManager
 import android.hardware.display.NightDisplayListener
 import android.os.Handler
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.res.R
@@ -45,7 +45,7 @@
 import org.mockito.MockitoAnnotations
 import org.mockito.Mockito.`when` as whenever
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 class NightDisplayTileTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java
index c391153..d6fa124 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java
@@ -22,9 +22,9 @@
 import static org.mockito.Mockito.when;
 
 import android.os.Handler;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.MetricsLogger;
@@ -45,7 +45,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class OneHandedModeTileTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
index d7beb5d..f8f82f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java
@@ -24,9 +24,9 @@
 
 import android.os.Handler;
 import android.service.quicksettings.Tile;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.MetricsLogger;
@@ -49,7 +49,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class QRCodeScannerTileTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index 122d9e4..914bd0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -52,9 +52,9 @@
 import android.service.quickaccesswallet.QuickAccessWalletService;
 import android.service.quickaccesswallet.WalletCard;
 import android.service.quicksettings.Tile;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.MetricsLogger;
@@ -83,7 +83,7 @@
 
 import java.util.Collections;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class QuickAccessWalletTileTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
index 37654d5..df59e57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
@@ -18,8 +18,8 @@
 
 import android.os.Handler
 import android.service.quicksettings.Tile
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.SysuiTestCase
@@ -33,6 +33,7 @@
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
 import com.android.systemui.recordissue.IssueRecordingState
 import com.android.systemui.recordissue.RecordIssueDialogDelegate
+import com.android.systemui.recordissue.TraceurMessageSender
 import com.android.systemui.res.R
 import com.android.systemui.settings.UserContextProvider
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil
@@ -41,6 +42,7 @@
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.whenever
 import com.google.common.truth.Truth.assertThat
+import java.util.concurrent.Executors
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -54,7 +56,7 @@
  * This class tests the functionality of the RecordIssueTile. The initial state of the tile is
  * always be inactive at the start of these tests.
  */
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 class RecordIssueTileTest : SysuiTestCase() {
@@ -70,6 +72,7 @@
     @Mock private lateinit var dialogLauncherAnimator: DialogTransitionAnimator
     @Mock private lateinit var panelInteractor: PanelInteractor
     @Mock private lateinit var userContextProvider: UserContextProvider
+    @Mock private lateinit var traceurMessageSender: TraceurMessageSender
     @Mock private lateinit var delegateFactory: RecordIssueDialogDelegate.Factory
     @Mock private lateinit var dialogDelegate: RecordIssueDialogDelegate
     @Mock private lateinit var dialog: SystemUIDialog
@@ -102,6 +105,8 @@
                 dialogLauncherAnimator,
                 panelInteractor,
                 userContextProvider,
+                traceurMessageSender,
+                Executors.newSingleThreadExecutor(),
                 issueRecordingState,
                 delegateFactory,
             )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
index 8eaa876..798e9fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
@@ -25,9 +25,9 @@
 
 import android.os.Handler;
 import android.service.quicksettings.Tile;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.R;
@@ -52,7 +52,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class ReduceBrightColorsTileTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
index c02fca7..4193063 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java
@@ -27,10 +27,10 @@
 import android.content.pm.PackageManager;
 import android.hardware.SensorPrivacyManager;
 import android.os.Handler;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableResources;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.MetricsLogger;
@@ -58,7 +58,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class RotationLockTileTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index 507fb86..0d12483 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -31,9 +31,9 @@
 import android.app.Dialog;
 import android.os.Handler;
 import android.service.quicksettings.Tile;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.MetricsLogger;
@@ -64,7 +64,7 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class ScreenRecordTileTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
index 47fc3ec..8324a73 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
@@ -21,8 +21,8 @@
 import android.content.res.Configuration
 import android.content.res.Resources
 import android.os.Handler
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.MetricsLogger
 import com.android.systemui.res.R
@@ -47,7 +47,7 @@
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 class UiModeNightTileTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt
index f9d69c2..c764c54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt
@@ -19,10 +19,10 @@
 import android.content.Context
 import android.content.pm.UserInfo
 import android.graphics.Bitmap
-import android.testing.AndroidTestingRunner
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.internal.util.UserIcons
@@ -45,7 +45,7 @@
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class UserDetailViewAdapterTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
index ff712ad..b888617 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
@@ -13,11 +13,11 @@
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableResources;
 import android.view.View;
 import android.widget.LinearLayout;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -39,7 +39,7 @@
 import java.util.List;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class InternetAdapterTest extends SysuiTestCase {
 
     private static final String WIFI_KEY = "Wi-Fi_Key";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
index 9798562..5273495 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
@@ -51,7 +51,6 @@
 import android.telephony.TelephonyCallback;
 import android.telephony.TelephonyDisplayInfo;
 import android.telephony.TelephonyManager;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableResources;
 import android.text.TextUtils;
@@ -59,6 +58,7 @@
 import android.view.View;
 import android.view.WindowManager;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.UiEventLogger;
@@ -101,7 +101,7 @@
 import java.util.Map;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class InternetDialogDelegateControllerTest extends SysuiTestCase {
 
@@ -1116,6 +1116,34 @@
         assertThat(mInternetDialogController.hasActiveSubIdOnDds()).isFalse();
     }
 
+    @Test
+    public void hasActiveSubIdOnDds_activeDdsAndIsOnlyNonTerrestrialNetwork_returnFalse() {
+        when(SubscriptionManager.getDefaultDataSubscriptionId())
+                .thenReturn(SUB_ID);
+        SubscriptionInfo info = mock(SubscriptionInfo.class);
+        when(info.isEmbedded()).thenReturn(true);
+        when(info.isOnlyNonTerrestrialNetwork()).thenReturn(true);
+        when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(info);
+
+        mInternetDialogController.mOnSubscriptionsChangedListener.onSubscriptionsChanged();
+
+        assertFalse(mInternetDialogController.hasActiveSubIdOnDds());
+    }
+
+    @Test
+    public void hasActiveSubIdOnDds_activeDdsAndIsNotOnlyNonTerrestrialNetwork_returnTrue() {
+        when(SubscriptionManager.getDefaultDataSubscriptionId())
+                .thenReturn(SUB_ID);
+        SubscriptionInfo info = mock(SubscriptionInfo.class);
+        when(info.isEmbedded()).thenReturn(true);
+        when(info.isOnlyNonTerrestrialNetwork()).thenReturn(false);
+        when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(info);
+
+        mInternetDialogController.mOnSubscriptionsChangedListener.onSubscriptionsChanged();
+
+        assertTrue(mInternetDialogController.hasActiveSubIdOnDds());
+    }
+
     private String getResourcesString(String name) {
         return mContext.getResources().getString(getResourcesId(name));
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
index aefcc87..ff8c448 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
@@ -18,7 +18,6 @@
 import android.content.Intent;
 import android.os.Handler;
 import android.telephony.TelephonyManager;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.View;
 import android.widget.LinearLayout;
@@ -26,6 +25,7 @@
 import android.widget.TextView;
 
 import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.dx.mockito.inline.extended.ExtendedMockito;
@@ -56,7 +56,7 @@
 
 @Ignore("b/257089187")
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class InternetDialogDelegateTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/WifiStateWorkerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/WifiStateWorkerTest.java
index 5d7ba7b..57484c2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/WifiStateWorkerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/WifiStateWorkerTest.java
@@ -35,8 +35,8 @@
 
 import android.content.Intent;
 import android.net.wifi.WifiManager;
-import android.testing.AndroidTestingRunner;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -52,7 +52,7 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 public class WifiStateWorkerTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
index e48d96b..ad6c64b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt
@@ -19,8 +19,8 @@
 import android.content.DialogInterface
 import android.content.Intent
 import android.provider.Settings
-import android.testing.AndroidTestingRunner
 import android.widget.Button
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.SysuiTestCase
@@ -53,7 +53,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class UserSwitchDialogControllerTest : SysuiTestCase() {
 
     @Mock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
index b75b318..3aaaf95 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayDialogControllerTest.java
@@ -29,12 +29,12 @@
 import android.content.res.Resources;
 import android.hardware.devicestate.DeviceState;
 import android.hardware.devicestate.DeviceStateManager;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.TextView;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -54,7 +54,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class RearDisplayDialogControllerTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
index 74deae3..fc74586 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recents/OverviewProxyServiceTest.kt
@@ -23,9 +23,9 @@
 import android.os.PowerManager
 import android.os.Process
 import android.os.UserHandle
-import android.testing.AndroidTestingRunner
 import android.testing.TestableContext
 import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.dx.mockito.inline.extended.ExtendedMockito
 import com.android.internal.app.AssistUtils
@@ -81,7 +81,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class OverviewProxyServiceTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
index fcc6b4f..ca60650 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/recordissue/RecordIssueDialogDelegateTest.kt
@@ -20,10 +20,10 @@
 import android.content.Context
 import android.content.SharedPreferences
 import android.os.UserHandle
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.widget.Button
 import android.widget.Switch
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.DialogTransitionAnimator
@@ -63,7 +63,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class RecordIssueDialogDelegateTest : SysuiTestCase() {
 
@@ -81,6 +81,7 @@
     @Mock private lateinit var sysuiState: SysUiState
     @Mock private lateinit var systemUIDialogManager: SystemUIDialogManager
     @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher
+    @Mock private lateinit var traceurMessageSender: TraceurMessageSender
     private val systemClock = FakeSystemClock()
     private val bgExecutor = FakeExecutor(systemClock)
     private val mainExecutor = FakeExecutor(systemClock)
@@ -131,6 +132,7 @@
                     userFileManager,
                     screenCaptureDisabledDialogDelegate,
                     issueRecordingState,
+                    traceurMessageSender
                 ) {
                     latch.countDown()
                 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/retail/data/repository/RetailModeSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/retail/data/repository/RetailModeSettingsRepositoryTest.kt
index fe80f70..ba7a65d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/retail/data/repository/RetailModeSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/retail/data/repository/RetailModeSettingsRepositoryTest.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.retail.data.repository
 
 import android.provider.Settings
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -33,7 +33,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class RetailModeSettingsRepositoryTest : SysuiTestCase() {
 
     private val globalSettings = FakeGlobalSettings()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/retail/domain/interactor/RetailModeInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/retail/domain/interactor/RetailModeInteractorImplTest.kt
index 8f13169..b536520 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/retail/domain/interactor/RetailModeInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/retail/domain/interactor/RetailModeInteractorImplTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.retail.domain.interactor
 
-import android.testing.AndroidTestingRunner
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.retail.data.repository.FakeRetailModeRepository
@@ -25,7 +25,7 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 class RetailModeInteractorImplTest : SysuiTestCase() {
 
     private val retailModeRepository = FakeRetailModeRepository()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index deecc5b..0d7a9e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -146,7 +146,8 @@
 
     @Test
     public void testLogStartPartialRecording() {
-        MediaProjectionCaptureTarget target = new MediaProjectionCaptureTarget(new LaunchCookie());
+        MediaProjectionCaptureTarget target =
+                new MediaProjectionCaptureTarget(new LaunchCookie(), 12345);
         Intent startIntent = RecordingService.getStartIntent(mContext, 0, 0, false, target);
         mRecordingService.onStartCommand(startIntent, 0, 0);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
index 1ef44f7..b8267a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/GlanceableHubContainerControllerTest.kt
@@ -18,6 +18,8 @@
 
 import android.graphics.Rect
 import android.os.PowerManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.testing.ViewUtils
@@ -30,13 +32,14 @@
 import com.android.compose.animation.scene.ObservableTransitionState
 import com.android.compose.animation.scene.SceneKey
 import com.android.systemui.Flags
+import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.ambient.touch.TouchHandler
 import com.android.systemui.ambient.touch.TouchMonitor
 import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent
 import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
 import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository
-import com.android.systemui.communal.data.repository.fakeCommunalRepository
+import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
 import com.android.systemui.communal.domain.interactor.communalInteractor
 import com.android.systemui.communal.domain.interactor.setCommunalAvailable
 import com.android.systemui.communal.shared.model.CommunalScenes
@@ -51,6 +54,7 @@
 import com.android.systemui.res.R
 import com.android.systemui.scene.shared.model.sceneDataSourceDelegator
 import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.any
 import com.google.common.truth.Truth.assertThat
@@ -64,9 +68,11 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyFloat
 import org.mockito.Mock
 import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
 
 @ExperimentalCoroutinesApi
@@ -99,7 +105,7 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
 
-        communalRepository = kosmos.fakeCommunalRepository
+        communalRepository = kosmos.fakeCommunalSceneRepository
 
         ambientTouchComponentFactory =
             object : AmbientTouchComponent.Factory {
@@ -124,6 +130,7 @@
                     ambientTouchComponentFactory,
                     communalContent,
                     kosmos.sceneDataSourceDelegator,
+                    kosmos.notificationStackScrollLayoutController
                 )
         }
         testableLooper = TestableLooper.get(this)
@@ -166,6 +173,7 @@
                         ambientTouchComponentFactory,
                         communalContent,
                         kosmos.sceneDataSourceDelegator,
+                        kosmos.notificationStackScrollLayoutController
                     )
 
                 // First call succeeds.
@@ -176,6 +184,7 @@
             }
         }
 
+    @DisableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
     @Test
     fun onTouchEvent_communalClosed_doesNotIntercept() =
         with(kosmos) {
@@ -187,6 +196,7 @@
             }
         }
 
+    @DisableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
     @Test
     fun onTouchEvent_openGesture_interceptsTouches() =
         with(kosmos) {
@@ -204,6 +214,7 @@
             }
         }
 
+    @DisableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
     @Test
     fun onTouchEvent_communalTransitioning_interceptsTouches() =
         with(kosmos) {
@@ -230,6 +241,7 @@
             }
         }
 
+    @DisableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
     @Test
     fun onTouchEvent_communalOpen_interceptsTouches() =
         with(kosmos) {
@@ -244,6 +256,7 @@
             }
         }
 
+    @DisableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
     @Test
     fun onTouchEvent_communalAndBouncerShowing_doesNotIntercept() =
         with(kosmos) {
@@ -262,6 +275,7 @@
             }
         }
 
+    @DisableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
     @Test
     fun onTouchEvent_communalAndShadeShowing_doesNotIntercept() =
         with(kosmos) {
@@ -278,6 +292,7 @@
             }
         }
 
+    @DisableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
     @Test
     fun onTouchEvent_containerViewDisposed_doesNotIntercept() =
         with(kosmos) {
@@ -310,6 +325,7 @@
                     ambientTouchComponentFactory,
                     communalContent,
                     kosmos.sceneDataSourceDelegator,
+                    kosmos.notificationStackScrollLayoutController,
                 )
 
             assertThat(underTest.lifecycle.currentState).isEqualTo(Lifecycle.State.INITIALIZED)
@@ -329,6 +345,7 @@
                     ambientTouchComponentFactory,
                     communalContent,
                     kosmos.sceneDataSourceDelegator,
+                    kosmos.notificationStackScrollLayoutController,
                 )
 
             // Only initView without attaching a view as we don't want the flows to start collecting
@@ -499,13 +516,30 @@
             }
         }
 
+    @Test
+    @EnableFlags(FLAG_GLANCEABLE_HUB_FULLSCREEN_SWIPE)
+    fun fullScreenSwipeGesture_doNotProcessTouchesInNotificationStack() =
+        with(kosmos) {
+            testScope.runTest {
+                // Communal is closed.
+                goToScene(CommunalScenes.Blank)
+                `when`(
+                        notificationStackScrollLayoutController.isBelowLastNotification(
+                            anyFloat(),
+                            anyFloat()
+                        )
+                    )
+                    .thenReturn(false)
+                assertThat(underTest.onTouchEvent(DOWN_EVENT)).isFalse()
+            }
+        }
+
     private fun initAndAttachContainerView() {
         containerView = View(context)
 
         parentView = FrameLayout(context)
-        parentView.addView(containerView)
 
-        underTest.initView(containerView)
+        parentView.addView(underTest.initView(containerView))
 
         // Attach the view so that flows start collecting.
         ViewUtils.attachView(parentView, CONTAINER_WIDTH, CONTAINER_HEIGHT)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 041adea..c3cedf8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -837,6 +837,7 @@
                 mJavaAdapter,
                 mCastController,
                 new ResourcesSplitShadeStateController(),
+                () -> mKosmos.getCommunalTransitionViewModel(),
                 () -> mLargeScreenHeaderHelper
         );
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
index 845744a..85541aa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerImplBaseTest.java
@@ -308,6 +308,7 @@
                 new JavaAdapter(mTestScope.getBackgroundScope()),
                 mCastController,
                 splitShadeStateController,
+                () -> mKosmos.getCommunalTransitionViewModel(),
                 () -> mLargeScreenHeaderHelper
         );
         mQsController.init();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
index 5363c57..308b370 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java
@@ -312,9 +312,10 @@
                 info = new CarrierTextManager.CarrierTextCallbackInfo(
                 "",
                 new CharSequence[]{""},
-                true,
+                /* anySimReady= */ true,
+                /* isInSatelliteMode= */ false,
                 new int[]{0},
-                true /* airplaneMode */);
+                /* airplaneMode= */ true);
         mCallback.updateCarrierInfo(info);
         mTestableLooper.processAllMessages();
         assertEquals(View.GONE, mShadeCarrierGroup.getNoSimTextView().getVisibility());
@@ -326,15 +327,59 @@
                 info = new CarrierTextManager.CarrierTextCallbackInfo(
                 "",
                 new CharSequence[]{FIRST_CARRIER_NAME, ""},
-                true,
+                /* anySimReady= */ true,
+                /* isInSatelliteMode= */ false,
                 new int[]{0, 1},
-                false /* airplaneMode */);
+                /* airplaneMode= */ false);
         mCallback.updateCarrierInfo(info);
         mTestableLooper.processAllMessages();
         assertEquals(View.VISIBLE, mShadeCarrierGroupController.getShadeCarrierVisibility(0));
     }
 
     @Test
+    public void isInSatelliteMode_true_noSimViewShownWithText() {
+        CarrierTextManager.CarrierTextCallbackInfo
+            info = new CarrierTextManager.CarrierTextCallbackInfo(
+            "Satellite Mode Test",
+            new CharSequence[]{FIRST_CARRIER_NAME},
+            /* anySimReady= */ true,
+            /* isInSatelliteMode= */ true,
+            new int[]{1},
+            /* airplaneMode= */ false);
+
+        mCallback.updateCarrierInfo(info);
+        mTestableLooper.processAllMessages();
+
+        assertThat(mShadeCarrierGroup.getNoSimTextView().getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mShadeCarrierGroup.getNoSimTextView().getText()).isEqualTo(
+                "Satellite Mode Test");
+
+        verify(mShadeCarrier1).setVisibility(View.GONE);
+        verify(mShadeCarrier2).setVisibility(View.GONE);
+        verify(mShadeCarrier3).setVisibility(View.GONE);
+    }
+
+    @Test
+    public void isInSatelliteMode_false_normalSimViewsShown() {
+        CarrierTextManager.CarrierTextCallbackInfo
+                info = new CarrierTextManager.CarrierTextCallbackInfo(
+                "Satellite Mode Test",
+                new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME},
+                /* anySimReady= */ true,
+                /* isInSatelliteMode= */ false,
+                new int[]{0, 1},
+                /* airplaneMode= */ false);
+
+        mCallback.updateCarrierInfo(info);
+        mTestableLooper.processAllMessages();
+
+        assertThat(mShadeCarrierGroup.getNoSimTextView().getVisibility()).isEqualTo(View.GONE);
+
+        verify(mShadeCarrier1).setVisibility(View.VISIBLE);
+        verify(mShadeCarrier2).setVisibility(View.VISIBLE);
+    }
+
+    @Test
     public void testListenerNotCalledOnRegistreation() {
         mShadeCarrierGroupController
                 .setOnSingleCarrierChangedListener(mOnSingleCarrierChangedListener);
@@ -350,8 +395,7 @@
                 SINGLE_CARRIER_TEXT,
                 new CharSequence[]{SINGLE_CARRIER_TEXT},
                 true,
-                new int[]{0},
-                false /* airplaneMode */);
+                new int[]{0});
 
         mCallback.updateCarrierInfo(info);
         mTestableLooper.processAllMessages();
@@ -369,8 +413,7 @@
                 MULTI_CARRIER_TEXT,
                 new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME},
                 true,
-                new int[]{0, 1},
-                false /* airplaneMode */);
+                new int[]{0, 1});
 
         mCallback.updateCarrierInfo(info);
         mTestableLooper.processAllMessages();
@@ -387,16 +430,14 @@
                 SINGLE_CARRIER_TEXT,
                 new CharSequence[]{FIRST_CARRIER_NAME},
                 true,
-                new int[]{0},
-                false /* airplaneMode */);
+                new int[]{0});
 
         CarrierTextManager.CarrierTextCallbackInfo
                 multiCarrierInfo = new CarrierTextManager.CarrierTextCallbackInfo(
                 MULTI_CARRIER_TEXT,
                 new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME},
                 true,
-                new int[]{0, 1},
-                false /* airplaneMode */);
+                new int[]{0, 1});
 
         mCallback.updateCarrierInfo(singleCarrierInfo);
         mTestableLooper.processAllMessages();
@@ -421,8 +462,7 @@
                 SINGLE_CARRIER_TEXT,
                 new CharSequence[]{FIRST_CARRIER_NAME},
                 true,
-                new int[]{0},
-                false /* airplaneMode */);
+                new int[]{0});
 
         mCallback.updateCarrierInfo(singleCarrierInfo);
         mTestableLooper.processAllMessages();
@@ -443,8 +483,7 @@
                 MULTI_CARRIER_TEXT,
                 new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME},
                 true,
-                new int[]{0, 1},
-                false /* airplaneMode */);
+                new int[]{0, 1});
 
         mCallback.updateCarrierInfo(multiCarrierInfo);
         mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java
index 22c9e45..6985a27 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutListSearchTest.java
@@ -20,14 +20,21 @@
 
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.graphics.drawable.Icon;
+import android.os.Handler;
+import android.platform.test.annotations.EnableFlags;
+import android.view.KeyboardShortcutGroup;
+import android.view.KeyboardShortcutInfo;
 import android.view.WindowManager;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 
 import com.google.android.material.bottomsheet.BottomSheetDialog;
@@ -36,10 +43,14 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
+import java.util.Arrays;
+import java.util.Collections;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class KeyboardShortcutListSearchTest extends SysuiTestCase {
@@ -51,6 +62,7 @@
 
     @Mock private BottomSheetDialog mBottomSheetDialog;
     @Mock WindowManager mWindowManager;
+    @Mock Handler mHandler;
 
     @Before
     public void setUp() {
@@ -58,6 +70,7 @@
         mKeyboardShortcutListSearch.sInstance = mKeyboardShortcutListSearch;
         mKeyboardShortcutListSearch.mKeyboardShortcutsBottomSheetDialog = mBottomSheetDialog;
         mKeyboardShortcutListSearch.mContext = mContext;
+        mKeyboardShortcutListSearch.mBackgroundHandler = mHandler;
     }
 
     @Test
@@ -78,4 +91,59 @@
         verify(mWindowManager).requestAppKeyboardShortcuts(any(), anyInt());
         verify(mWindowManager).requestImeKeyboardShortcuts(any(), anyInt());
     }
+
+    @Test
+    @EnableFlags(Flags.FLAG_VALIDATE_KEYBOARD_SHORTCUT_HELPER_ICON_URI)
+    public void requestAppKeyboardShortcuts_callback_sanitisesIcons() {
+        KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
+
+        mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID);
+
+        ArgumentCaptor<WindowManager.KeyboardShortcutsReceiver> callbackCaptor =
+                ArgumentCaptor.forClass(WindowManager.KeyboardShortcutsReceiver.class);
+        ArgumentCaptor<Runnable> handlerRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(mWindowManager).requestAppKeyboardShortcuts(callbackCaptor.capture(), anyInt());
+        callbackCaptor.getValue().onKeyboardShortcutsReceived(Collections.singletonList(group));
+        verify(mHandler).post(handlerRunnableCaptor.capture());
+        handlerRunnableCaptor.getValue().run();
+
+        verify(group.getItems().get(0)).clearIcon();
+        verify(group.getItems().get(1)).clearIcon();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_VALIDATE_KEYBOARD_SHORTCUT_HELPER_ICON_URI)
+    public void requestImeKeyboardShortcuts_callback_sanitisesIcons() {
+        KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
+
+        mKeyboardShortcutListSearch.toggle(mContext, DEVICE_ID);
+
+        ArgumentCaptor<WindowManager.KeyboardShortcutsReceiver> callbackCaptor =
+                ArgumentCaptor.forClass(WindowManager.KeyboardShortcutsReceiver.class);
+        ArgumentCaptor<Runnable> handlerRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(mWindowManager).requestImeKeyboardShortcuts(callbackCaptor.capture(), anyInt());
+        callbackCaptor.getValue().onKeyboardShortcutsReceived(Collections.singletonList(group));
+        verify(mHandler).post(handlerRunnableCaptor.capture());
+        handlerRunnableCaptor.getValue().run();
+
+        verify(group.getItems().get(0)).clearIcon();
+        verify(group.getItems().get(1)).clearIcon();
+
+    }
+
+    private KeyboardShortcutGroup createKeyboardShortcutGroupForIconTests() {
+        Icon icon = mock(Icon.class);
+
+        KeyboardShortcutInfo info1 = mock(KeyboardShortcutInfo.class);
+        KeyboardShortcutInfo info2 = mock(KeyboardShortcutInfo.class);
+        when(info1.getIcon()).thenReturn(icon);
+        when(info2.getIcon()).thenReturn(icon);
+        when(info1.getLabel()).thenReturn("label");
+        when(info2.getLabel()).thenReturn("label");
+
+        KeyboardShortcutGroup group = new KeyboardShortcutGroup("label",
+                Arrays.asList(new KeyboardShortcutInfo[]{ info1, info2}));
+        group.setPackageName("com.example");
+        return group;
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
index a3ecde0..2b3f139 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsTest.java
@@ -20,25 +20,36 @@
 
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.Dialog;
+import android.graphics.drawable.Icon;
+import android.os.Handler;
+import android.platform.test.annotations.EnableFlags;
+import android.view.KeyboardShortcutGroup;
+import android.view.KeyboardShortcutInfo;
 import android.view.WindowManager;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 
+import java.util.Arrays;
+import java.util.Collections;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class KeyboardShortcutsTest extends SysuiTestCase {
@@ -50,6 +61,7 @@
 
     @Mock private Dialog mDialog;
     @Mock WindowManager mWindowManager;
+    @Mock Handler mHandler;
 
     @Before
     public void setUp() {
@@ -57,6 +69,7 @@
         mKeyboardShortcuts.sInstance = mKeyboardShortcuts;
         mKeyboardShortcuts.mKeyboardShortcutsDialog = mDialog;
         mKeyboardShortcuts.mContext = mContext;
+        mKeyboardShortcuts.mBackgroundHandler = mHandler;
     }
 
     @Test
@@ -77,4 +90,78 @@
         verify(mWindowManager).requestAppKeyboardShortcuts(any(), anyInt());
         verify(mWindowManager).requestImeKeyboardShortcuts(any(), anyInt());
     }
+
+    @Test
+    public void sanitiseShortcuts_clearsIcons() {
+        KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
+
+        KeyboardShortcuts.sanitiseShortcuts(Collections.singletonList(group));
+
+        verify(group.getItems().get(0)).clearIcon();
+        verify(group.getItems().get(1)).clearIcon();
+    }
+
+    @Test
+    public void sanitiseShortcuts_nullPackage_clearsIcons() {
+        KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
+        group.setPackageName(null);
+
+        KeyboardShortcuts.sanitiseShortcuts(Collections.singletonList(group));
+
+        verify(group.getItems().get(0)).clearIcon();
+        verify(group.getItems().get(1)).clearIcon();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_VALIDATE_KEYBOARD_SHORTCUT_HELPER_ICON_URI)
+    public void requestAppKeyboardShortcuts_callback_sanitisesIcons() {
+        KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
+
+        mKeyboardShortcuts.toggle(mContext, DEVICE_ID);
+
+        ArgumentCaptor<WindowManager.KeyboardShortcutsReceiver> callbackCaptor =
+                ArgumentCaptor.forClass(WindowManager.KeyboardShortcutsReceiver.class);
+        ArgumentCaptor<Runnable> handlerRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(mWindowManager).requestAppKeyboardShortcuts(callbackCaptor.capture(), anyInt());
+        callbackCaptor.getValue().onKeyboardShortcutsReceived(Collections.singletonList(group));
+        verify(mHandler).post(handlerRunnableCaptor.capture());
+        handlerRunnableCaptor.getValue().run();
+
+        verify(group.getItems().get(0)).clearIcon();
+        verify(group.getItems().get(1)).clearIcon();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_VALIDATE_KEYBOARD_SHORTCUT_HELPER_ICON_URI)
+    public void requestImeKeyboardShortcuts_callback_sanitisesIcons() {
+        KeyboardShortcutGroup group = createKeyboardShortcutGroupForIconTests();
+
+        mKeyboardShortcuts.toggle(mContext, DEVICE_ID);
+
+        ArgumentCaptor<WindowManager.KeyboardShortcutsReceiver> callbackCaptor =
+                ArgumentCaptor.forClass(WindowManager.KeyboardShortcutsReceiver.class);
+        ArgumentCaptor<Runnable> handlerRunnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        verify(mWindowManager).requestImeKeyboardShortcuts(callbackCaptor.capture(), anyInt());
+        callbackCaptor.getValue().onKeyboardShortcutsReceived(Collections.singletonList(group));
+        verify(mHandler).post(handlerRunnableCaptor.capture());
+        handlerRunnableCaptor.getValue().run();
+
+        verify(group.getItems().get(0)).clearIcon();
+        verify(group.getItems().get(1)).clearIcon();
+
+    }
+
+    private KeyboardShortcutGroup createKeyboardShortcutGroupForIconTests() {
+        Icon icon = mock(Icon.class);
+
+        KeyboardShortcutInfo info1 = mock(KeyboardShortcutInfo.class);
+        KeyboardShortcutInfo info2 = mock(KeyboardShortcutInfo.class);
+        when(info1.getIcon()).thenReturn(icon);
+        when(info2.getIcon()).thenReturn(icon);
+
+        KeyboardShortcutGroup group = new KeyboardShortcutGroup("label",
+                Arrays.asList(new KeyboardShortcutInfo[]{ info1, info2}));
+        group.setPackageName("com.example");
+        return group;
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
new file mode 100644
index 0000000..0f33b9d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.chips.mediaprojection.domain.interactor
+
+import androidx.test.filters.SmallTest
+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.testScope
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.viewmodel.mediaProjectionChipInteractor
+import com.android.systemui.util.time.fakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+
+@SmallTest
+class MediaProjectionChipInteractorTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
+    private val testScope = kosmos.testScope
+    private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository
+    private val systemClock = kosmos.fakeSystemClock
+
+    private val underTest = kosmos.mediaProjectionChipInteractor
+
+    @Test
+    fun chip_notProjectingState_isHidden() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+        }
+
+    @Test
+    fun chip_singleTaskState_isShownWithIcon() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            mediaProjectionRepo.mediaProjectionState.value =
+                MediaProjectionState.SingleTask(createTask(taskId = 1))
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            val icon = (latest as OngoingActivityChipModel.Shown).icon
+            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected)
+        }
+
+    @Test
+    fun chip_entireScreenState_isShownWithIcon() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            val icon = (latest as OngoingActivityChipModel.Shown).icon
+            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected)
+        }
+
+    @Test
+    fun chip_timeResetsOnEachNewShare() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            systemClock.setElapsedRealtime(1234)
+            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(1234)
+
+            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
+
+            systemClock.setElapsedRealtime(5678)
+            mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen
+
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(5678)
+        }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index 1260f07..121229c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -23,6 +23,9 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask
 import com.android.systemui.res.R
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
 import com.android.systemui.screenrecord.data.repository.screenRecordRepository
@@ -35,13 +38,20 @@
 class OngoingActivityChipsViewModelTest : SysuiTestCase() {
 
     private val kosmos = Kosmos()
+    private val testScope = kosmos.testScope
+
+    private val screenRecordState = kosmos.screenRecordRepository.screenRecordState
+    private val mediaProjectionState = kosmos.fakeMediaProjectionRepository.mediaProjectionState
+    private val callState = kosmos.callChipInteractor.chip
+
     private val underTest = kosmos.ongoingActivityChipsViewModel
 
     @Test
     fun chip_allHidden_hidden() =
-        kosmos.testScope.runTest {
-            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.DoingNothing
-            kosmos.callChipInteractor.chip.value = OngoingActivityChipModel.Hidden
+        testScope.runTest {
+            screenRecordState.value = ScreenRecordModel.DoingNothing
+            mediaProjectionState.value = MediaProjectionState.NotProjecting
+            callState.value = OngoingActivityChipModel.Hidden
 
             val latest by collectLastValue(underTest.chip)
 
@@ -50,9 +60,10 @@
 
     @Test
     fun chip_screenRecordShow_restHidden_screenRecordShown() =
-        kosmos.testScope.runTest {
-            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
-            kosmos.callChipInteractor.chip.value = OngoingActivityChipModel.Hidden
+        testScope.runTest {
+            screenRecordState.value = ScreenRecordModel.Recording
+            mediaProjectionState.value = MediaProjectionState.NotProjecting
+            callState.value = OngoingActivityChipModel.Hidden
 
             val latest by collectLastValue(underTest.chip)
 
@@ -61,15 +72,15 @@
 
     @Test
     fun chip_screenRecordShowAndCallShow_screenRecordShown() =
-        kosmos.testScope.runTest {
-            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
+        testScope.runTest {
+            screenRecordState.value = ScreenRecordModel.Recording
 
             val callChip =
                 OngoingActivityChipModel.Shown(
                     Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")),
                     startTimeMs = 600L,
                 ) {}
-            kosmos.callChipInteractor.chip.value = callChip
+            callState.value = callChip
 
             val latest by collectLastValue(underTest.chip)
 
@@ -77,16 +88,46 @@
         }
 
     @Test
-    fun chip_screenRecordHideAndCallShown_callShown() =
-        kosmos.testScope.runTest {
-            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.DoingNothing
+    fun chip_screenRecordShowAndMediaProjectionShow_screenRecordShown() =
+        testScope.runTest {
+            screenRecordState.value = ScreenRecordModel.Recording
+            mediaProjectionState.value = MediaProjectionState.EntireScreen
+            callState.value = OngoingActivityChipModel.Hidden
+
+            val latest by collectLastValue(underTest.chip)
+
+            assertIsScreenRecordChip(latest)
+        }
+
+    @Test
+    fun chip_mediaProjectionShowAndCallShow_mediaProjectionShown() =
+        testScope.runTest {
+            screenRecordState.value = ScreenRecordModel.DoingNothing
+            mediaProjectionState.value = MediaProjectionState.EntireScreen
+            val callChip =
+                OngoingActivityChipModel.Shown(
+                    Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")),
+                    startTimeMs = 600L,
+                ) {}
+            callState.value = callChip
+
+            val latest by collectLastValue(underTest.chip)
+
+            assertIsMediaProjectionChip(latest)
+        }
+
+    @Test
+    fun chip_screenRecordAndMediaProjectionHideAndCallShown_callShown() =
+        testScope.runTest {
+            screenRecordState.value = ScreenRecordModel.DoingNothing
+            mediaProjectionState.value = MediaProjectionState.NotProjecting
 
             val callChip =
                 OngoingActivityChipModel.Shown(
                     Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")),
                     startTimeMs = 600L,
                 ) {}
-            kosmos.callChipInteractor.chip.value = callChip
+            callState.value = callChip
 
             val latest by collectLastValue(underTest.chip)
 
@@ -95,22 +136,29 @@
 
     @Test
     fun chip_higherPriorityChipAdded_lowerPriorityChipReplaced() =
-        kosmos.testScope.runTest {
+        testScope.runTest {
             // Start with just the lower priority call chip
             val callChip =
                 OngoingActivityChipModel.Shown(
                     Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")),
                     startTimeMs = 600L,
                 ) {}
-            kosmos.callChipInteractor.chip.value = callChip
-            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.DoingNothing
+            callState.value = callChip
+            mediaProjectionState.value = MediaProjectionState.NotProjecting
+            screenRecordState.value = ScreenRecordModel.DoingNothing
 
             val latest by collectLastValue(underTest.chip)
 
             assertThat(latest).isEqualTo(callChip)
 
+            // WHEN the higher priority media projection chip is added
+            mediaProjectionState.value = MediaProjectionState.SingleTask(createTask(taskId = 1))
+
+            // THEN the higher priority media projection chip is used
+            assertIsMediaProjectionChip(latest)
+
             // WHEN the higher priority screen record chip is added
-            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
+            screenRecordState.value = ScreenRecordModel.Recording
 
             // THEN the higher priority screen record chip is used
             assertIsScreenRecordChip(latest)
@@ -118,31 +166,47 @@
 
     @Test
     fun chip_highestPriorityChipRemoved_showsNextPriorityChip() =
-        kosmos.testScope.runTest {
-            // Start with both the higher priority screen record chip and lower priority call chip
-            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
+        testScope.runTest {
+            // WHEN all chips are active
+            screenRecordState.value = ScreenRecordModel.Recording
+            mediaProjectionState.value = MediaProjectionState.EntireScreen
 
             val callChip =
                 OngoingActivityChipModel.Shown(
                     Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")),
                     startTimeMs = 600L,
                 ) {}
-            kosmos.callChipInteractor.chip.value = callChip
+            callState.value = callChip
 
             val latest by collectLastValue(underTest.chip)
 
+            // THEN the highest priority screen record is used
             assertIsScreenRecordChip(latest)
 
             // WHEN the higher priority screen record is removed
-            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.DoingNothing
+            screenRecordState.value = ScreenRecordModel.DoingNothing
+
+            // THEN the lower priority media projection is used
+            assertIsMediaProjectionChip(latest)
+
+            // WHEN the higher priority media projection is removed
+            mediaProjectionState.value = MediaProjectionState.NotProjecting
 
             // THEN the lower priority call is used
             assertThat(latest).isEqualTo(callChip)
         }
 
-    private fun assertIsScreenRecordChip(latest: OngoingActivityChipModel?) {
-        assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
-        val icon = (latest as OngoingActivityChipModel.Shown).icon
-        assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.stat_sys_screen_record)
+    companion object {
+        fun assertIsScreenRecordChip(latest: OngoingActivityChipModel?) {
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            val icon = (latest as OngoingActivityChipModel.Shown).icon
+            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.stat_sys_screen_record)
+        }
+
+        fun assertIsMediaProjectionChip(latest: OngoingActivityChipModel?) {
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+            val icon = (latest as OngoingActivityChipModel.Shown).icon
+            assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected)
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index 63ce233..068e166 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -32,6 +32,7 @@
 import android.os.UserHandle
 import android.platform.test.annotations.DisableFlags
 import android.provider.Settings
+import android.testing.TestableLooper.RunWithLooper
 import android.view.View
 import android.widget.FrameLayout
 import androidx.test.filters.SmallTest
@@ -84,6 +85,7 @@
 import java.util.concurrent.Executor
 
 @SmallTest
+@RunWithLooper(setAsMainLooper = true)
 class LockscreenSmartspaceControllerTest : SysuiTestCase() {
     companion object {
         const val SMARTSPACE_TIME_TOO_EARLY = 1000L
@@ -778,6 +780,7 @@
     }
 
     @Test
+    @RunWithLooper(setAsMainLooper = false)
     fun testConnectAttemptBeforeInitializationShouldNotCreateSession() {
         // GIVEN an uninitalized smartspaceView
         // WHEN the device is provisioned
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
index 8012768..dc9c22f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
@@ -320,7 +320,7 @@
                 .thenReturn(1);
         ArgumentCaptor<ContentObserver> contentObserverCaptor = ArgumentCaptor.forClass(
                 ContentObserver.class);
-        verify(mSecureSettings).registerContentObserverForUser(eq(SHOW_NOTIFICATION_SNOOZE),
+        verify(mSecureSettings).registerContentObserverForUserSync(eq(SHOW_NOTIFICATION_SNOOZE),
                 contentObserverCaptor.capture(), anyInt());
         ContentObserver contentObserver = contentObserverCaptor.getValue();
         contentObserver.onChange(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
index 7943872..2f77b33 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
@@ -69,22 +69,21 @@
     private val groupMembershipManager: GroupMembershipManager = mock()
 
     private val section = NotifSection(mock(), 0)
-    private val entry = NotificationEntryBuilder()
-        .setSection(section)
-        .setParent(GroupEntry.ROOT_ENTRY)
-        .build()
+    private val entry =
+        NotificationEntryBuilder().setSection(section).setParent(GroupEntry.ROOT_ENTRY).build()
 
     private lateinit var contentObserver: ContentObserver
 
-    private val adjustmentProvider = NotifUiAdjustmentProvider(
-        handler,
-        secureSettings,
-        lockscreenUserManager,
-        sensitiveNotifProtectionController,
-        sectionStyleProvider,
-        userTracker,
-        groupMembershipManager,
-    )
+    private val adjustmentProvider =
+        NotifUiAdjustmentProvider(
+            handler,
+            secureSettings,
+            lockscreenUserManager,
+            sensitiveNotifProtectionController,
+            sectionStyleProvider,
+            userTracker,
+            groupMembershipManager,
+        )
 
     @Before
     fun setup() {
@@ -92,9 +91,8 @@
         adjustmentProvider.addDirtyListener(dirtyListener)
         verify(secureSettings).getIntForUser(eq(SHOW_NOTIFICATION_SNOOZE), any(), any())
         contentObserver = withArgCaptor {
-            verify(secureSettings).registerContentObserverForUser(
-                eq(SHOW_NOTIFICATION_SNOOZE), capture(), any()
-            )
+            verify(secureSettings)
+                .registerContentObserverForUserSync(eq(SHOW_NOTIFICATION_SNOOZE), capture(), any())
         }
         verifyNoMoreInteractions(secureSettings, dirtyListener)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
index 7903a73..e984200 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/VisualInterruptionDecisionProviderImplTest.kt
@@ -91,7 +91,8 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager)
+            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager,
+                    uiEventLogger)
         ) {
             ensurePeekState()
             assertShouldHeadsUp(
@@ -110,7 +111,8 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager)
+            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager,
+                    uiEventLogger)
         ) {
             ensurePeekState()
             assertShouldNotHeadsUp(
@@ -129,7 +131,8 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager)
+            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager,
+                    uiEventLogger)
         ) {
             ensurePeekState()
             assertShouldHeadsUp(
@@ -146,7 +149,8 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager)
+            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager,
+                    uiEventLogger)
         ) {
             ensurePeekState()
             assertShouldHeadsUp(
@@ -163,7 +167,8 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager)
+            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager,
+                    uiEventLogger)
         ) {
             ensurePeekState()
             assertShouldHeadsUp(
@@ -180,7 +185,8 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager)
+            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager,
+                    uiEventLogger)
         ) {
             ensurePeekState()
             assertShouldHeadsUp(
@@ -197,7 +203,8 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager)
+            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager,
+                    uiEventLogger)
         ) {
             assertFsiNotSuppressed()
         }
@@ -208,7 +215,8 @@
         avalancheProvider.startTime = whenAgo(10)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager)
+            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager,
+                    uiEventLogger)
         ) {
             ensurePeekState()
             assertShouldHeadsUp(
@@ -232,7 +240,8 @@
         ).thenReturn(PERMISSION_GRANTED)
 
         withFilter(
-            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager)
+            AvalancheSuppressor(avalancheProvider, systemClock, systemSettings, packageManager,
+                    uiEventLogger)
         ) {
             ensurePeekState()
             assertShouldHeadsUp(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index a355cd1..0d3ab86 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -27,6 +27,7 @@
 import static org.junit.Assert.assertNull;
 import static org.junit.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.mock;
 import static org.mockito.Mockito.spy;
@@ -50,7 +51,6 @@
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.filters.Suppress;
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.media.controls.util.MediaFeatureFlag;
@@ -81,7 +81,6 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 @RunWithLooper(setAsMainLooper = true)
-@Suppress
 public class NotificationContentInflaterTest extends SysuiTestCase {
 
     private NotificationContentInflater mNotificationInflater;
@@ -128,7 +127,7 @@
                 TestableLooper.get(this));
         ExpandableNotificationRow row = mHelper.createRow(mBuilder.build());
         mRow = spy(row);
-        when(mNotifLayoutInflaterFactoryProvider.provide(any(), any()))
+        when(mNotifLayoutInflaterFactoryProvider.provide(any(), anyInt()))
                 .thenReturn(mNotifLayoutInflaterFactory);
 
         mNotificationInflater = new NotificationContentInflater(
@@ -140,7 +139,7 @@
                 mSmartReplyStateInflater,
                 mNotifLayoutInflaterFactoryProvider,
                 mHeadsUpStyleProvider,
-                mock(NotificationContentInflaterLogger.class));
+                mock(NotificationRowContentBinderLogger.class));
     }
 
     @Test
@@ -265,7 +264,7 @@
                                 R.layout.custom_view_dark);
                     }
                 },
-                mock(NotificationContentInflaterLogger.class));
+                mock(NotificationRowContentBinderLogger.class));
         assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS));
     }
 
@@ -282,6 +281,7 @@
     }
 
     @Test
+    @Ignore
     public void testUsesSameViewWhenCachedPossibleToReuse() throws Exception {
         // GIVEN a cached view.
         RemoteViews contractedRemoteView = mBuilder.createContentView();
@@ -347,6 +347,7 @@
     }
 
     @Test
+    @Ignore
     public void testNotificationViewHeightTooSmallFailsValidation() {
         View view = mock(View.class);
         when(view.getHeight())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt
index 352b79f..310fa67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt
@@ -102,6 +102,7 @@
         verify(userTracker).addCallback(any(), any())
         verify(dumpManager).registerNormalDumpable(anyString(), eq(controller))
     }
+
     @Test
     fun updateContentObserverRegistration_onUserChange_noSettingsListeners() {
         verify(userTracker).addCallback(capture(userTrackerCallbackCaptor), any())
@@ -112,10 +113,11 @@
         userCallback.onUserChanged(userId, context)
 
         // Validate: Nothing to do, since we aren't monitoring settings
-        verify(secureSettings, never()).unregisterContentObserver(any())
+        verify(secureSettings, never()).unregisterContentObserverSync(any())
         verify(secureSettings, never())
-            .registerContentObserverForUser(any(Uri::class.java), anyBoolean(), any(), anyInt())
+            .registerContentObserverForUserSync(any(Uri::class.java), anyBoolean(), any(), anyInt())
     }
+
     @Test
     fun updateContentObserverRegistration_onUserChange_withSettingsListeners() {
         // When: someone is listening to a setting
@@ -129,9 +131,9 @@
         userCallback.onUserChanged(userId, context)
 
         // Validate: The tracker is unregistered and re-registered with the new user
-        verify(secureSettings).unregisterContentObserver(any())
+        verify(secureSettings).unregisterContentObserverSync(any())
         verify(secureSettings)
-            .registerContentObserverForUser(eq(settingUri1), eq(false), any(), eq(userId))
+            .registerContentObserverForUserSync(eq(settingUri1), eq(false), any(), eq(userId))
     }
 
     @Test
@@ -140,7 +142,7 @@
         verifyZeroInteractions(secureSettings)
         testableLooper.processAllMessages()
         verify(secureSettings)
-            .registerContentObserverForUser(
+            .registerContentObserverForUserSync(
                 eq(settingUri1),
                 eq(false),
                 any(),
@@ -149,7 +151,7 @@
 
         controller.addCallback(settingUri1, Mockito.mock(Listener::class.java))
         verify(secureSettings)
-            .registerContentObserverForUser(any(Uri::class.java), anyBoolean(), any(), anyInt())
+            .registerContentObserverForUserSync(any(Uri::class.java), anyBoolean(), any(), anyInt())
     }
 
     @Test
@@ -158,7 +160,7 @@
         verifyZeroInteractions(secureSettings)
         testableLooper.processAllMessages()
         verify(secureSettings)
-            .registerContentObserverForUser(
+            .registerContentObserverForUserSync(
                 eq(settingUri1),
                 eq(false),
                 any(),
@@ -170,7 +172,7 @@
         verifyNoMoreInteractions(secureSettings)
         testableLooper.processAllMessages()
         verify(secureSettings)
-            .registerContentObserverForUser(
+            .registerContentObserverForUserSync(
                 eq(settingUri2),
                 eq(false),
                 any(),
@@ -186,7 +188,7 @@
         verifyZeroInteractions(secureSettings)
         testableLooper.processAllMessages()
         verify(secureSettings)
-            .registerContentObserverForUser(
+            .registerContentObserverForUserSync(
                 eq(settingUri1),
                 eq(false),
                 any(),
@@ -198,18 +200,18 @@
         verifyNoMoreInteractions(secureSettings)
         testableLooper.processAllMessages()
         verify(secureSettings)
-            .registerContentObserverForUser(eq(settingUri2), anyBoolean(), any(), anyInt())
+            .registerContentObserverForUserSync(eq(settingUri2), anyBoolean(), any(), anyInt())
         clearInvocations(secureSettings)
 
         controller.removeCallback(settingUri2, listenerSetting2)
         testableLooper.processAllMessages()
-        verify(secureSettings, never()).unregisterContentObserver(any())
+        verify(secureSettings, never()).unregisterContentObserverSync(any())
         clearInvocations(secureSettings)
 
         controller.removeCallback(settingUri1, listenerSetting1)
         verifyNoMoreInteractions(secureSettings)
         testableLooper.processAllMessages()
-        verify(secureSettings).unregisterContentObserver(any())
+        verify(secureSettings).unregisterContentObserverSync(any())
     }
 
     @Test
@@ -253,7 +255,7 @@
         testableLooper.processAllMessages()
 
         verify(secureSettings)
-            .registerContentObserverForUser(
+            .registerContentObserverForUserSync(
                 any(Uri::class.java),
                 anyBoolean(),
                 capture(settingsObserverCaptor),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 1661860..65941ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -192,7 +192,7 @@
                 mBgCoroutineContext,
                 mMainCoroutineContext);
 
-        NotificationContentInflater contentBinder = new NotificationContentInflater(
+        NotificationRowContentBinder contentBinder = new NotificationContentInflater(
                 mock(NotifRemoteViewCache.class),
                 mock(NotificationRemoteInputManager.class),
                 mock(ConversationNotificationProcessor.class),
@@ -201,7 +201,7 @@
                 new MockSmartReplyInflater(),
                 mock(NotifLayoutInflaterFactory.Provider.class),
                 mock(HeadsUpStyleProvider.class),
-                mock(NotificationContentInflaterLogger.class));
+                mock(NotificationRowContentBinderLogger.class));
         contentBinder.setInflateSynchronously(true);
         mBindStage = new RowContentBindStage(contentBinder,
                 mock(NotifInflationErrorManager.class),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt
index e025d3d..d366632 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/SingleLineViewInflaterTest.kt
@@ -372,7 +372,7 @@
         }
 
         // Inflate the SingleLineViewModel
-        // Mock the behavior of NotificationContentInflater.doInBackground
+        // Mock the behavior of NotificationRowContentBinder.doInBackground
         val messagingStyle = builder.getMessagingStyle()
         val isConversation = type is OneToOneConversation || type is GroupConversation
         return SingleLineViewInflater.inflateSingleLineViewModel(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index f461e2f..12f3ef3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -171,7 +171,6 @@
         //  and then we would test both configurations, but currently they are all read
         //  in the constructor.
         mSetFlagsRule.enableFlags(FLAG_NEW_AOD_TRANSITION);
-        mFeatureFlags.setDefault(Flags.UNCLEARED_TRANSIENT_HUN_FIX);
 
         // Inject dependencies before initializing the layout
         mDependency.injectTestDependency(FeatureFlags.class, mFeatureFlags);
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 62804ed1..7ea85a1 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
@@ -133,11 +133,13 @@
 import com.android.systemui.power.domain.interactor.PowerInteractor;
 import com.android.systemui.res.R;
 import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor;
+import com.android.systemui.scene.domain.startable.ScrimStartable;
 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.settings.brightness.BrightnessSliderController;
 import com.android.systemui.settings.brightness.domain.interactor.BrightnessMirrorShowingInteractor;
 import com.android.systemui.shade.CameraLauncher;
+import com.android.systemui.shade.GlanceableHubContainerController;
 import com.android.systemui.shade.NotificationPanelView;
 import com.android.systemui.shade.NotificationPanelViewController;
 import com.android.systemui.shade.NotificationShadeWindowViewController;
@@ -253,7 +255,6 @@
     @Mock private IDreamManager mDreamManager;
     @Mock private LightRevealScrimViewModel mLightRevealScrimViewModel;
     @Mock private LightRevealScrim mLightRevealScrim;
-    @Mock private ScrimController mScrimController;
     @Mock private DozeScrimController mDozeScrimController;
     @Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
     @Mock private BiometricUnlockController mBiometricUnlockController;
@@ -337,6 +338,7 @@
     @Mock private KeyboardShortcuts mKeyboardShortcuts;
     @Mock private KeyboardShortcutListSearch mKeyboardShortcutListSearch;
     @Mock private PackageManager mPackageManager;
+    @Mock private GlanceableHubContainerController mGlanceableHubContainerController;
 
     private ShadeController mShadeController;
     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
@@ -353,6 +355,7 @@
 
     private final BrightnessMirrorShowingInteractor mBrightnessMirrorShowingInteractor =
             mKosmos.getBrightnessMirrorShowingInteractor();
+    private ScrimController mScrimController;
 
     @Before
     public void setup() throws Exception {
@@ -470,6 +473,8 @@
         when(mUserTracker.getUserHandle()).thenReturn(
                 UserHandle.of(ActivityManager.getCurrentUser()));
 
+        mScrimController = mKosmos.getScrimController();
+
         createCentralSurfaces();
     }
 
@@ -590,7 +595,8 @@
                 mUserTracker,
                 () -> mFingerprintManager,
                 mActivityStarter,
-                mBrightnessMirrorShowingInteractor
+                mBrightnessMirrorShowingInteractor,
+                mGlanceableHubContainerController
         );
         mScreenLifecycle.addObserver(mCentralSurfaces.mScreenObserver);
         mCentralSurfaces.initShadeVisibilityListener();
@@ -730,7 +736,7 @@
     @Test
     public void testFingerprintNotification_UpdatesScrims() {
         mCentralSurfaces.notifyBiometricAuthModeChanged();
-        verify(mScrimController).transitionTo(any(), any());
+        verify(mScrimController).legacyTransitionTo(any(), any());
     }
 
     @Test
@@ -739,7 +745,7 @@
         when(mBiometricUnlockController.getMode())
                 .thenReturn(BiometricUnlockController.MODE_WAKE_AND_UNLOCK);
         mCentralSurfaces.updateScrimController();
-        verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any());
+        verify(mScrimController).legacyTransitionTo(eq(ScrimState.UNLOCKED), any());
     }
 
     @Test
@@ -750,7 +756,7 @@
         // Starting a pulse should change the scrim controller to the pulsing state
         when(mCameraLauncher.isLaunchingAffordance()).thenReturn(true);
         mCentralSurfaces.updateScrimController();
-        verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any());
+        verify(mScrimController).legacyTransitionTo(eq(ScrimState.UNLOCKED), any());
     }
 
     @Test
@@ -786,7 +792,7 @@
         // Starting a pulse should change the scrim controller to the pulsing state
         when(mCameraLauncher.isLaunchingAffordance()).thenReturn(false);
         mCentralSurfaces.updateScrimController();
-        verify(mScrimController).transitionTo(eq(ScrimState.KEYGUARD));
+        verify(mScrimController).legacyTransitionTo(eq(ScrimState.KEYGUARD));
     }
 
     @Test
@@ -814,12 +820,12 @@
         // Starting a pulse should change the scrim controller to the pulsing state
         when(mDozeServiceHost.isPulsing()).thenReturn(true);
         mCentralSurfaces.updateScrimController();
-        verify(mScrimController).transitionTo(eq(ScrimState.PULSING), any());
+        verify(mScrimController).legacyTransitionTo(eq(ScrimState.PULSING), any());
 
         // Ending a pulse should take it back to keyguard state
         when(mDozeServiceHost.isPulsing()).thenReturn(false);
         mCentralSurfaces.updateScrimController();
-        verify(mScrimController).transitionTo(eq(ScrimState.KEYGUARD));
+        verify(mScrimController).legacyTransitionTo(eq(ScrimState.KEYGUARD));
     }
 
     @Test
@@ -830,7 +836,7 @@
 
         mCentralSurfaces.updateScrimController();
 
-        verify(mScrimController, times(2)).transitionTo(eq(ScrimState.AOD));
+        verify(mScrimController, times(2)).legacyTransitionTo(eq(ScrimState.AOD));
         verify(mStatusBarKeyguardViewManager).onKeyguardFadedAway();
     }
 
@@ -842,7 +848,7 @@
 
         mCentralSurfaces.updateScrimController();
 
-        verify(mScrimController).transitionTo(eq(ScrimState.AUTH_SCRIMMED_SHADE));
+        verify(mScrimController).legacyTransitionTo(eq(ScrimState.AUTH_SCRIMMED_SHADE));
         verify(mStatusBarKeyguardViewManager).onKeyguardFadedAway();
     }
 
@@ -858,7 +864,7 @@
 
         mCentralSurfaces.updateScrimController();
 
-        verify(mScrimController).transitionTo(eq(ScrimState.AUTH_SCRIMMED));
+        verify(mScrimController).legacyTransitionTo(eq(ScrimState.AUTH_SCRIMMED));
     }
 
     @Test
@@ -874,7 +880,7 @@
         mCentralSurfaces.updateScrimController();
 
         // Tests the safeguard to reset the scrimstate
-        verify(mScrimController, never()).transitionTo(any());
+        verify(mScrimController, never()).legacyTransitionTo(any());
     }
 
     @Test
@@ -890,7 +896,7 @@
         mCentralSurfaces.updateScrimController();
 
         // Tests the safeguard to reset the scrimstate
-        verify(mScrimController, never()).transitionTo(eq(ScrimState.KEYGUARD));
+        verify(mScrimController, never()).legacyTransitionTo(eq(ScrimState.KEYGUARD));
     }
 
     @Test
@@ -905,7 +911,7 @@
 
         mCentralSurfaces.updateScrimController();
 
-        verify(mScrimController).transitionTo(eq(ScrimState.AUTH_SCRIMMED_SHADE));
+        verify(mScrimController).legacyTransitionTo(eq(ScrimState.AUTH_SCRIMMED_SHADE));
     }
 
     @Test
@@ -917,7 +923,7 @@
         mTestScope.getTestScheduler().runCurrent();
 
         // ScrimState also transitions.
-        verify(mScrimController).transitionTo(ScrimState.GLANCEABLE_HUB);
+        verify(mScrimController).legacyTransitionTo(ScrimState.GLANCEABLE_HUB);
 
         // Transition away from the glanceable hub.
         mKosmos.getCommunalRepository()
@@ -926,7 +932,7 @@
         mTestScope.getTestScheduler().runCurrent();
 
         // ScrimState goes back to UNLOCKED.
-        verify(mScrimController).transitionTo(eq(ScrimState.UNLOCKED), any());
+        verify(mScrimController).legacyTransitionTo(eq(ScrimState.UNLOCKED), any());
     }
 
     @Test
@@ -942,7 +948,7 @@
         mTestScope.getTestScheduler().runCurrent();
 
         // ScrimState also transitions.
-        verify(mScrimController).transitionTo(ScrimState.GLANCEABLE_HUB_OVER_DREAM);
+        verify(mScrimController).legacyTransitionTo(ScrimState.GLANCEABLE_HUB_OVER_DREAM);
 
         // Transition away from the glanceable hub.
         mKosmos.getCommunalRepository()
@@ -951,7 +957,7 @@
         mTestScope.getTestScheduler().runCurrent();
 
         // ScrimState goes back to UNLOCKED.
-        verify(mScrimController).transitionTo(eq(ScrimState.DREAMING));
+        verify(mScrimController).legacyTransitionTo(eq(ScrimState.DREAMING));
     }
 
     @Test
@@ -1161,6 +1167,8 @@
     @EnableSceneContainer
     public void brightnesShowingChanged_flagEnabled_ScrimControllerNotified() {
         mCentralSurfaces.registerCallbacks();
+        final ScrimStartable scrimStartable = mKosmos.getScrimStartable();
+        scrimStartable.start();
 
         mBrightnessMirrorShowingInteractor.setMirrorShowing(true);
         mTestScope.getTestScheduler().runCurrent();
@@ -1170,7 +1178,7 @@
         mTestScope.getTestScheduler().runCurrent();
         ArgumentCaptor<ScrimState> captor = ArgumentCaptor.forClass(ScrimState.class);
         // The default is to call the one with the callback argument
-        verify(mScrimController, atLeastOnce()).transitionTo(captor.capture(), any());
+        verify(mScrimController, atLeastOnce()).transitionTo(captor.capture());
         assertThat(captor.getValue()).isNotEqualTo(ScrimState.BRIGHTNESS_MIRROR);
     }
 
@@ -1181,8 +1189,9 @@
 
         mBrightnessMirrorShowingInteractor.setMirrorShowing(true);
         mTestScope.getTestScheduler().runCurrent();
-        verify(mScrimController, never()).transitionTo(ScrimState.BRIGHTNESS_MIRROR);
-        verify(mScrimController, never()).transitionTo(eq(ScrimState.BRIGHTNESS_MIRROR), any());
+        verify(mScrimController, never()).legacyTransitionTo(ScrimState.BRIGHTNESS_MIRROR);
+        verify(mScrimController, never())
+                .legacyTransitionTo(eq(ScrimState.BRIGHTNESS_MIRROR), any());
     }
 
     @Test
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 416a869..4590071 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
@@ -309,7 +309,7 @@
         mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false);
         mTestScope.getTestScheduler().runCurrent();
 
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
         finishAnimationsImmediately();
     }
 
@@ -327,7 +327,7 @@
 
     @Test
     public void transitionToKeyguard() {
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
         finishAnimationsImmediately();
 
         assertScrimAlpha(Map.of(
@@ -342,7 +342,7 @@
 
     @Test
     public void transitionToShadeLocked() {
-        mScrimController.transitionTo(SHADE_LOCKED);
+        mScrimController.legacyTransitionTo(SHADE_LOCKED);
         mScrimController.setQsPosition(1f, 0);
         finishAnimationsImmediately();
 
@@ -360,7 +360,7 @@
     @Test
     public void transitionToShadeLocked_clippingQs() {
         mScrimController.setClipsQsScrim(true);
-        mScrimController.transitionTo(SHADE_LOCKED);
+        mScrimController.legacyTransitionTo(SHADE_LOCKED);
         mScrimController.setQsPosition(1f, 0);
         finishAnimationsImmediately();
 
@@ -377,7 +377,7 @@
 
     @Test
     public void transitionToOff() {
-        mScrimController.transitionTo(ScrimState.OFF);
+        mScrimController.legacyTransitionTo(ScrimState.OFF);
         finishAnimationsImmediately();
 
         assertScrimAlpha(Map.of(
@@ -394,7 +394,7 @@
 
     @Test
     public void transitionToAod_withRegularWallpaper() {
-        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.legacyTransitionTo(ScrimState.AOD);
         finishAnimationsImmediately();
 
         assertScrimAlpha(Map.of(
@@ -414,7 +414,7 @@
         mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
         mTestScope.getTestScheduler().runCurrent();
 
-        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.legacyTransitionTo(ScrimState.AOD);
         finishAnimationsImmediately();
 
         assertScrimAlpha(Map.of(
@@ -423,7 +423,7 @@
         assertEquals(0f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
         // Pulsing notification should conserve AOD wallpaper.
-        mScrimController.transitionTo(ScrimState.PULSING);
+        mScrimController.legacyTransitionTo(ScrimState.PULSING);
         finishAnimationsImmediately();
 
         assertScrimAlpha(Map.of(
@@ -438,7 +438,7 @@
         mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
         mTestScope.getTestScheduler().runCurrent();
 
-        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.legacyTransitionTo(ScrimState.AOD);
         finishAnimationsImmediately();
 
         assertScrimAlpha(Map.of(
@@ -457,7 +457,7 @@
         mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
         mTestScope.getTestScheduler().runCurrent();
 
-        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.legacyTransitionTo(ScrimState.AOD);
         finishAnimationsImmediately();
         mScrimController.setHasBackdrop(true);
         finishAnimationsImmediately();
@@ -476,7 +476,7 @@
     @Test
     public void transitionToAod_withFrontAlphaUpdates() {
         // Assert that setting the AOD front scrim alpha doesn't take effect in a non-AOD state.
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
         mScrimController.setAodFrontScrimAlpha(0.5f);
         finishAnimationsImmediately();
 
@@ -485,7 +485,7 @@
                 mScrimBehind, SEMI_TRANSPARENT));
 
         // ... but that it does take effect once we enter the AOD state.
-        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.legacyTransitionTo(ScrimState.AOD);
         finishAnimationsImmediately();
         assertScrimAlpha(Map.of(
                 mScrimInFront, SEMI_TRANSPARENT,
@@ -501,8 +501,8 @@
 
         // ... and make sure we recall the previous front scrim alpha even if we transition away
         // for a bit.
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
-        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
+        mScrimController.legacyTransitionTo(ScrimState.AOD);
         finishAnimationsImmediately();
         assertScrimAlpha(Map.of(
                 mScrimInFront, OPAQUE,
@@ -512,8 +512,8 @@
         // ... and alpha updates should be completely ignored if always_on is off.
         // Passing it forward would mess up the wake-up transition.
         mAlwaysOnEnabled = false;
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
-        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
+        mScrimController.legacyTransitionTo(ScrimState.AOD);
         finishAnimationsImmediately();
         mScrimController.setAodFrontScrimAlpha(0.3f);
         assertEquals(ScrimState.AOD.getFrontAlpha(), mScrimInFront.getViewAlpha(), 0.001f);
@@ -523,7 +523,7 @@
     @Test
     public void transitionToAod_afterDocked_ignoresAlwaysOnAndUpdatesFrontAlpha() {
         // Assert that setting the AOD front scrim alpha doesn't take effect in a non-AOD state.
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
         mScrimController.setAodFrontScrimAlpha(0.5f);
         finishAnimationsImmediately();
 
@@ -533,7 +533,7 @@
 
         // ... and doesn't take effect when disabled always_on
         mAlwaysOnEnabled = false;
-        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.legacyTransitionTo(ScrimState.AOD);
         finishAnimationsImmediately();
         assertScrimAlpha(Map.of(
                 mScrimInFront, OPAQUE,
@@ -542,9 +542,9 @@
 
         // ... but will take effect after docked
         when(mDockManager.isDocked()).thenReturn(true);
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
         mScrimController.setAodFrontScrimAlpha(0.5f);
-        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.legacyTransitionTo(ScrimState.AOD);
         finishAnimationsImmediately();
 
         assertScrimAlpha(Map.of(
@@ -572,14 +572,14 @@
         mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false);
         mTestScope.getTestScheduler().runCurrent();
 
-        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.legacyTransitionTo(ScrimState.AOD);
         finishAnimationsImmediately();
         assertScrimAlpha(Map.of(
                 mScrimInFront, TRANSPARENT,
                 mScrimBehind, TRANSPARENT));
         assertEquals(1f, mScrimController.getState().getMaxLightRevealScrimAlpha(), 0f);
 
-        mScrimController.transitionTo(ScrimState.PULSING);
+        mScrimController.legacyTransitionTo(ScrimState.PULSING);
         finishAnimationsImmediately();
         // Front scrim should be transparent, but tinted
         // Back scrim should be semi-transparent so the user can see the wallpaper
@@ -617,7 +617,7 @@
 
     @Test
     public void transitionToKeyguardBouncer() {
-        mScrimController.transitionTo(BOUNCER);
+        mScrimController.legacyTransitionTo(BOUNCER);
         finishAnimationsImmediately();
         // Front scrim should be transparent
         // Back scrim should be visible and tinted to the surface color
@@ -638,7 +638,7 @@
     @Test
     public void lockscreenToHubTransition_setsBehindScrimAlpha() {
         // Start on lockscreen.
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
         finishAnimationsImmediately();
 
         // Behind scrim starts at default alpha.
@@ -684,7 +684,7 @@
     @Test
     public void hubToLockscreenTransition_setsViewAlpha() {
         // Start on glanceable hub.
-        mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB);
+        mScrimController.legacyTransitionTo(ScrimState.GLANCEABLE_HUB);
         finishAnimationsImmediately();
 
         // Behind scrim starts at 0 alpha.
@@ -731,7 +731,7 @@
     public void transitionToHub() {
         mScrimController.setRawPanelExpansionFraction(0f);
         mScrimController.setBouncerHiddenFraction(KeyguardBouncerConstants.EXPANSION_HIDDEN);
-        mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB);
+        mScrimController.legacyTransitionTo(ScrimState.GLANCEABLE_HUB);
         finishAnimationsImmediately();
 
         // All scrims transparent on the hub.
@@ -743,7 +743,7 @@
 
     @Test
     public void openBouncerOnHub() {
-        mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB);
+        mScrimController.legacyTransitionTo(ScrimState.GLANCEABLE_HUB);
 
         // Open the bouncer.
         mScrimController.setRawPanelExpansionFraction(0f);
@@ -760,7 +760,7 @@
 
         // Bouncer is closed.
         mScrimController.setBouncerHiddenFraction(KeyguardBouncerConstants.EXPANSION_HIDDEN);
-        mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB);
+        mScrimController.legacyTransitionTo(ScrimState.GLANCEABLE_HUB);
         finishAnimationsImmediately();
 
         // All scrims are transparent.
@@ -772,7 +772,7 @@
 
     @Test
     public void openShadeOnHub() {
-        mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB);
+        mScrimController.legacyTransitionTo(ScrimState.GLANCEABLE_HUB);
 
         // Open the shade.
         mScrimController.setQsPosition(1f, 0);
@@ -802,7 +802,7 @@
     public void transitionToHubOverDream() {
         mScrimController.setRawPanelExpansionFraction(0f);
         mScrimController.setBouncerHiddenFraction(KeyguardBouncerConstants.EXPANSION_HIDDEN);
-        mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB_OVER_DREAM);
+        mScrimController.legacyTransitionTo(ScrimState.GLANCEABLE_HUB_OVER_DREAM);
         finishAnimationsImmediately();
 
         // All scrims transparent on the hub.
@@ -814,7 +814,7 @@
 
     @Test
     public void openBouncerOnHubOverDream() {
-        mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB_OVER_DREAM);
+        mScrimController.legacyTransitionTo(ScrimState.GLANCEABLE_HUB_OVER_DREAM);
 
         // Open the bouncer.
         mScrimController.setRawPanelExpansionFraction(0f);
@@ -831,7 +831,7 @@
 
         // Bouncer is closed.
         mScrimController.setBouncerHiddenFraction(KeyguardBouncerConstants.EXPANSION_HIDDEN);
-        mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB_OVER_DREAM);
+        mScrimController.legacyTransitionTo(ScrimState.GLANCEABLE_HUB_OVER_DREAM);
         finishAnimationsImmediately();
 
         // All scrims are transparent.
@@ -843,7 +843,7 @@
 
     @Test
     public void openShadeOnHubOverDream() {
-        mScrimController.transitionTo(ScrimState.GLANCEABLE_HUB_OVER_DREAM);
+        mScrimController.legacyTransitionTo(ScrimState.GLANCEABLE_HUB_OVER_DREAM);
 
         // Open the shade.
         mScrimController.setQsPosition(1f, 0);
@@ -880,7 +880,7 @@
     @Test
     public void onThemeChangeWhileClipQsScrim_bouncerBehindTint_remainsBlack() {
         mScrimController.setClipsQsScrim(true);
-        mScrimController.transitionTo(BOUNCER);
+        mScrimController.legacyTransitionTo(BOUNCER);
         finishAnimationsImmediately();
 
         assertEquals(BOUNCER.getBehindTint(), Color.BLACK);
@@ -892,7 +892,7 @@
     @Test
     public void transitionToKeyguardBouncer_clippingQs() {
         mScrimController.setClipsQsScrim(true);
-        mScrimController.transitionTo(BOUNCER);
+        mScrimController.legacyTransitionTo(BOUNCER);
         finishAnimationsImmediately();
         // Front scrim should be transparent
         // Back scrim should be clipping QS
@@ -912,7 +912,7 @@
     @Test
     public void disableClipQsScrimWithoutStateTransition_updatesTintAndAlpha() {
         mScrimController.setClipsQsScrim(true);
-        mScrimController.transitionTo(BOUNCER);
+        mScrimController.legacyTransitionTo(BOUNCER);
 
         mScrimController.setClipsQsScrim(false);
 
@@ -934,7 +934,7 @@
     @Test
     public void enableClipQsScrimWithoutStateTransition_updatesTintAndAlpha() {
         mScrimController.setClipsQsScrim(false);
-        mScrimController.transitionTo(BOUNCER);
+        mScrimController.legacyTransitionTo(BOUNCER);
 
         mScrimController.setClipsQsScrim(true);
 
@@ -955,7 +955,7 @@
 
     @Test
     public void transitionToBouncer() {
-        mScrimController.transitionTo(ScrimState.BOUNCER_SCRIMMED);
+        mScrimController.legacyTransitionTo(ScrimState.BOUNCER_SCRIMMED);
         finishAnimationsImmediately();
         assertScrimAlpha(Map.of(
                 mScrimInFront, OPAQUE,
@@ -970,7 +970,7 @@
     public void transitionToUnlocked_clippedQs() {
         mScrimController.setClipsQsScrim(true);
         mScrimController.setRawPanelExpansionFraction(0f);
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
         finishAnimationsImmediately();
 
         assertScrimTinted(Map.of(
@@ -1000,7 +1000,7 @@
     public void transitionToUnlocked_nonClippedQs_followsLargeScreensInterpolator() {
         mScrimController.setClipsQsScrim(false);
         mScrimController.setRawPanelExpansionFraction(0f);
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
         finishAnimationsImmediately();
 
         assertScrimTinted(Map.of(
@@ -1037,15 +1037,15 @@
 
     @Test
     public void scrimStateCallback() {
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
         finishAnimationsImmediately();
         assertEquals(mScrimState, ScrimState.UNLOCKED);
 
-        mScrimController.transitionTo(BOUNCER);
+        mScrimController.legacyTransitionTo(BOUNCER);
         finishAnimationsImmediately();
         assertEquals(mScrimState, BOUNCER);
 
-        mScrimController.transitionTo(ScrimState.BOUNCER_SCRIMMED);
+        mScrimController.legacyTransitionTo(ScrimState.BOUNCER_SCRIMMED);
         finishAnimationsImmediately();
         assertEquals(mScrimState, ScrimState.BOUNCER_SCRIMMED);
     }
@@ -1054,7 +1054,7 @@
     public void panelExpansion() {
         mScrimController.setRawPanelExpansionFraction(0f);
         mScrimController.setRawPanelExpansionFraction(0.5f);
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
         finishAnimationsImmediately();
 
         reset(mScrimBehind);
@@ -1114,7 +1114,7 @@
     public void panelExpansionAffectsAlpha() {
         mScrimController.setRawPanelExpansionFraction(0f);
         mScrimController.setRawPanelExpansionFraction(0.5f);
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
         finishAnimationsImmediately();
 
         final float scrimAlpha = mScrimBehind.getViewAlpha();
@@ -1135,10 +1135,10 @@
     @Test
     public void transitionToUnlockedFromOff() {
         // Simulate unlock with fingerprint without AOD
-        mScrimController.transitionTo(ScrimState.OFF);
+        mScrimController.legacyTransitionTo(ScrimState.OFF);
         mScrimController.setRawPanelExpansionFraction(0f);
         finishAnimationsImmediately();
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
 
         finishAnimationsImmediately();
 
@@ -1157,10 +1157,10 @@
     @Test
     public void transitionToUnlockedFromAod() {
         // Simulate unlock with fingerprint
-        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.legacyTransitionTo(ScrimState.AOD);
         mScrimController.setRawPanelExpansionFraction(0f);
         finishAnimationsImmediately();
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
 
         finishAnimationsImmediately();
 
@@ -1179,9 +1179,9 @@
     @Test
     public void scrimBlanksBeforeLeavingAod() {
         // Simulate unlock with fingerprint
-        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.legacyTransitionTo(ScrimState.AOD);
         finishAnimationsImmediately();
-        mScrimController.transitionTo(ScrimState.UNLOCKED,
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED,
                 new ScrimController.Callback() {
                     @Override
                     public void onDisplayBlanked() {
@@ -1203,9 +1203,9 @@
     public void scrimBlankCallbackWhenUnlockingFromPulse() {
         boolean[] blanked = {false};
         // Simulate unlock with fingerprint
-        mScrimController.transitionTo(ScrimState.PULSING);
+        mScrimController.legacyTransitionTo(ScrimState.PULSING);
         finishAnimationsImmediately();
-        mScrimController.transitionTo(ScrimState.UNLOCKED,
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED,
                 new ScrimController.Callback() {
                     @Override
                     public void onDisplayBlanked() {
@@ -1251,16 +1251,16 @@
         mScrimController.setHasBackdrop(false);
         mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false);
         mTestScope.getTestScheduler().runCurrent();
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
         finishAnimationsImmediately();
 
         // WHEN Simulate unlock with fingerprint
-        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.legacyTransitionTo(ScrimState.AOD);
         finishAnimationsImmediately();
 
         // WHEN transitioning to UNLOCKED, onDisplayCallbackBlanked callback called to continue
         // the transition but the scrim was not actually blanked
-        mScrimController.transitionTo(ScrimState.UNLOCKED,
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED,
                 new ScrimController.Callback() {
                     @Override
                     public void onDisplayBlanked() {
@@ -1279,7 +1279,7 @@
     public void testScrimCallback() {
         int[] callOrder = {0, 0, 0};
         int[] currentCall = {0};
-        mScrimController.transitionTo(ScrimState.AOD, new ScrimController.Callback() {
+        mScrimController.legacyTransitionTo(ScrimState.AOD, new ScrimController.Callback() {
             @Override
             public void onStart() {
                 callOrder[0] = ++currentCall[0];
@@ -1310,19 +1310,19 @@
     @Test
     public void testScrimCallbackCancelled() {
         boolean[] cancelledCalled = {false};
-        mScrimController.transitionTo(ScrimState.AOD, new ScrimController.Callback() {
+        mScrimController.legacyTransitionTo(ScrimState.AOD, new ScrimController.Callback() {
             @Override
             public void onCancelled() {
                 cancelledCalled[0] = true;
             }
         });
-        mScrimController.transitionTo(ScrimState.PULSING);
+        mScrimController.legacyTransitionTo(ScrimState.PULSING);
         Assert.assertTrue("onCancelled should have been called", cancelledCalled[0]);
     }
 
     @Test
     public void testHoldsWakeLock_whenAOD() {
-        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.legacyTransitionTo(ScrimState.AOD);
         verify(mWakeLock).acquire(anyString());
         verify(mWakeLock, never()).release(anyString());
         finishAnimationsImmediately();
@@ -1331,24 +1331,24 @@
 
     @Test
     public void testDoesNotHoldWakeLock_whenUnlocking() {
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
         finishAnimationsImmediately();
         verifyZeroInteractions(mWakeLock);
     }
 
     @Test
     public void testCallbackInvokedOnSameStateTransition() {
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
         finishAnimationsImmediately();
         ScrimController.Callback callback = mock(ScrimController.Callback.class);
-        mScrimController.transitionTo(ScrimState.UNLOCKED, callback);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED, callback);
         verify(callback).onFinished();
     }
 
     @Test
     public void testHoldsAodWallpaperAnimationLock() {
         // Pre-conditions
-        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.legacyTransitionTo(ScrimState.AOD);
         finishAnimationsImmediately();
         reset(mWakeLock);
 
@@ -1362,7 +1362,7 @@
     @Test
     public void testHoldsPulsingWallpaperAnimationLock() {
         // Pre-conditions
-        mScrimController.transitionTo(ScrimState.PULSING);
+        mScrimController.legacyTransitionTo(ScrimState.PULSING);
         finishAnimationsImmediately();
         reset(mWakeLock);
 
@@ -1378,9 +1378,9 @@
         mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
         mTestScope.getTestScheduler().runCurrent();
 
-        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.legacyTransitionTo(ScrimState.AOD);
         verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any());
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
         verify(mAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class));
     }
 
@@ -1391,22 +1391,22 @@
         mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
         mTestScope.getTestScheduler().runCurrent();
 
-        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.legacyTransitionTo(ScrimState.AOD);
 
         verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any());
     }
 
     @Test
     public void testConservesExpansionOpacityAfterTransition() {
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
         mScrimController.setRawPanelExpansionFraction(0.5f);
         finishAnimationsImmediately();
 
         final float expandedAlpha = mScrimBehind.getViewAlpha();
 
-        mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR);
+        mScrimController.legacyTransitionTo(ScrimState.BRIGHTNESS_MIRROR);
         finishAnimationsImmediately();
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
         finishAnimationsImmediately();
 
         assertEquals("Scrim expansion opacity wasn't conserved when transitioning back",
@@ -1415,12 +1415,12 @@
 
     @Test
     public void testCancelsOldAnimationBeforeBlanking() {
-        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.legacyTransitionTo(ScrimState.AOD);
         finishAnimationsImmediately();
         // Consume whatever value we had before
         mAnimatorListener.reset();
 
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
         finishAnimationsImmediately();
         Assert.assertTrue("Animators not canceled", mAnimatorListener.getNumCancels() != 0);
     }
@@ -1439,13 +1439,13 @@
         mTestScope.getTestScheduler().runCurrent();
 
         mScrimController.setKeyguardOccluded(true);
-        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.legacyTransitionTo(ScrimState.AOD);
         finishAnimationsImmediately();
         assertScrimAlpha(Map.of(
                 mScrimInFront, TRANSPARENT,
                 mScrimBehind, OPAQUE));
 
-        mScrimController.transitionTo(ScrimState.PULSING);
+        mScrimController.legacyTransitionTo(ScrimState.PULSING);
         finishAnimationsImmediately();
         assertScrimAlpha(Map.of(
                 mScrimInFront, TRANSPARENT,
@@ -1457,7 +1457,7 @@
         mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true);
         mTestScope.getTestScheduler().runCurrent();
 
-        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.legacyTransitionTo(ScrimState.AOD);
         finishAnimationsImmediately();
         assertScrimAlpha(Map.of(
                 mScrimInFront, TRANSPARENT,
@@ -1478,7 +1478,7 @@
             if (state == ScrimState.UNINITIALIZED) {
                 continue;
             }
-            mScrimController.transitionTo(state);
+            mScrimController.legacyTransitionTo(state);
             finishAnimationsImmediately();
             assertEquals("Should be clickable unless AOD or PULSING, was: " + state,
                     mScrimBehind.getViewAlpha() != 0 && !eatsTouches.contains(state),
@@ -1520,7 +1520,7 @@
 
     @Test
     public void testScrimsOpaque_whenShadeFullyExpanded() {
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
         mScrimController.setRawPanelExpansionFraction(1);
         // notifications scrim alpha change require calling setQsPosition
         mScrimController.setQsPosition(0, 300);
@@ -1536,7 +1536,7 @@
     public void testAuthScrim_setClipQSScrimTrue_notifScrimOpaque_whenShadeFullyExpanded() {
         // GIVEN device has an activity showing ('UNLOCKED' state can occur on the lock screen
         // with the camera app occluding the keyguard)
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
         mScrimController.setClipsQsScrim(true);
         mScrimController.setRawPanelExpansionFraction(1);
         // notifications scrim alpha change require calling setQsPosition
@@ -1544,7 +1544,7 @@
         finishAnimationsImmediately();
 
         // WHEN the user triggers the auth bouncer
-        mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
+        mScrimController.legacyTransitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
         finishAnimationsImmediately();
 
         assertEquals("Behind scrim should be opaque",
@@ -1564,7 +1564,7 @@
     public void testAuthScrim_setClipQSScrimFalse_notifScrimOpaque_whenShadeFullyExpanded() {
         // GIVEN device has an activity showing ('UNLOCKED' state can occur on the lock screen
         // with the camera app occluding the keyguard)
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
         mScrimController.setClipsQsScrim(false);
         mScrimController.setRawPanelExpansionFraction(1);
         // notifications scrim alpha change require calling setQsPosition
@@ -1572,7 +1572,7 @@
         finishAnimationsImmediately();
 
         // WHEN the user triggers the auth bouncer
-        mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
+        mScrimController.legacyTransitionTo(ScrimState.AUTH_SCRIMMED_SHADE);
         finishAnimationsImmediately();
 
         assertEquals("Behind scrim should be opaque",
@@ -1590,11 +1590,11 @@
     @Test
     public void testAuthScrimKeyguard() {
         // GIVEN device is on the keyguard
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
         finishAnimationsImmediately();
 
         // WHEN the user triggers the auth bouncer
-        mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED);
+        mScrimController.legacyTransitionTo(ScrimState.AUTH_SCRIMMED);
         finishAnimationsImmediately();
 
         // THEN the front scrim is updated and the KEYGUARD scrims are the same as the
@@ -1608,7 +1608,7 @@
     @Test
     public void testScrimsVisible_whenShadeVisible() {
         mScrimController.setClipsQsScrim(true);
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
         mScrimController.setRawPanelExpansionFraction(0.3f);
         // notifications scrim alpha change require calling setQsPosition
         mScrimController.setQsPosition(0, 300);
@@ -1643,7 +1643,7 @@
     @Test
     public void testScrimsVisible_whenShadeVisible_clippingQs() {
         mScrimController.setClipsQsScrim(true);
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
         mScrimController.setRawPanelExpansionFraction(0.3f);
         // notifications scrim alpha change require calling setQsPosition
         mScrimController.setQsPosition(0.5f, 300);
@@ -1657,7 +1657,7 @@
 
     @Test
     public void testScrimsVisible_whenShadeVisibleOnLockscreen() {
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
         mScrimController.setQsPosition(0.25f, 300);
 
         assertScrimAlpha(Map.of(
@@ -1668,7 +1668,7 @@
 
     @Test
     public void testNotificationScrimTransparent_whenOnLockscreen() {
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
         // even if shade is not pulled down, panel has expansion of 1 on the lockscreen
         mScrimController.setRawPanelExpansionFraction(1);
         mScrimController.setQsPosition(0f, /*qs panel bottom*/ 0);
@@ -1681,7 +1681,7 @@
     @Test
     public void testNotificationScrimVisible_afterOpeningShadeFromLockscreen() {
         mScrimController.setRawPanelExpansionFraction(1);
-        mScrimController.transitionTo(SHADE_LOCKED);
+        mScrimController.legacyTransitionTo(SHADE_LOCKED);
         finishAnimationsImmediately();
 
         assertScrimAlpha(Map.of(
@@ -1695,7 +1695,7 @@
         // clipping doesn't change tested logic but allows to assert scrims more in line with
         // their expected large screen behaviour
         mScrimController.setClipsQsScrim(false);
-        mScrimController.transitionTo(SHADE_LOCKED);
+        mScrimController.legacyTransitionTo(SHADE_LOCKED);
 
         mScrimController.setQsPosition(1f, 100 /* value doesn't matter */);
         assertTintAfterExpansion(mScrimBehind, SHADE_LOCKED.getBehindTint(), /* expansion= */ 1f);
@@ -1709,7 +1709,7 @@
     public void expansionNotificationAlpha_shadeLocked_bouncerActive_usesBouncerInterpolator() {
         when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(true);
 
-        mScrimController.transitionTo(SHADE_LOCKED);
+        mScrimController.legacyTransitionTo(SHADE_LOCKED);
 
         float expansion = 0.8f;
         float expectedAlpha =
@@ -1725,7 +1725,7 @@
     public void expansionNotificationAlpha_shadeLocked_bouncerNotActive_usesShadeInterpolator() {
         when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false);
 
-        mScrimController.transitionTo(SHADE_LOCKED);
+        mScrimController.legacyTransitionTo(SHADE_LOCKED);
 
         float expansion = 0.8f;
         float expectedAlpha = ShadeInterpolation.getNotificationScrimAlpha(expansion);
@@ -1740,7 +1740,7 @@
     public void notificationAlpha_unnocclusionAnimating_bouncerNotActive_usesKeyguardNotifAlpha() {
         when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false);
 
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
 
         assertAlphaAfterExpansion(
                 mNotificationsScrim, ScrimState.KEYGUARD.getNotifAlpha(), /* expansion */ 0f);
@@ -1760,7 +1760,7 @@
         when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(true);
         mScrimController.setClipsQsScrim(true);
 
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
 
         float expansion = 0.8f;
         float alpha = 1 - BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion);
@@ -1780,7 +1780,7 @@
         when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false);
         mScrimController.setClipsQsScrim(true);
 
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
 
         float expansion = 0.8f;
         float alpha = 1 - ShadeInterpolation.getNotificationScrimAlpha(expansion);
@@ -1800,7 +1800,7 @@
         when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false);
         mScrimController.setClipsQsScrim(false);
 
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
         finishAnimationsImmediately();
         assertThat(mScrimBehind.getTint())
                 .isEqualTo(ScrimState.KEYGUARD.getBehindTint());
@@ -1810,7 +1810,7 @@
     public void testNotificationTransparency_followsTransitionToFullShade() {
         mScrimController.setClipsQsScrim(true);
 
-        mScrimController.transitionTo(SHADE_LOCKED);
+        mScrimController.legacyTransitionTo(SHADE_LOCKED);
         mScrimController.setRawPanelExpansionFraction(1.0f);
         finishAnimationsImmediately();
 
@@ -1821,7 +1821,7 @@
         ));
 
         float shadeLockedAlpha = mNotificationsScrim.getViewAlpha();
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
         mScrimController.setRawPanelExpansionFraction(1.0f);
         finishAnimationsImmediately();
         float keyguardAlpha = mNotificationsScrim.getViewAlpha();
@@ -1849,10 +1849,10 @@
 
     @Test
     public void notificationTransparency_followsNotificationScrimProgress() {
-        mScrimController.transitionTo(SHADE_LOCKED);
+        mScrimController.legacyTransitionTo(SHADE_LOCKED);
         mScrimController.setRawPanelExpansionFraction(1.0f);
         finishAnimationsImmediately();
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
         mScrimController.setRawPanelExpansionFraction(1.0f);
         finishAnimationsImmediately();
 
@@ -1866,7 +1866,7 @@
     @Test
     public void notificationAlpha_qsNotClipped_alphaMatchesNotificationExpansionProgress() {
         mScrimController.setClipsQsScrim(false);
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
         // RawPanelExpansion and QsExpansion are usually used for the notification alpha
         // calculation.
         // Here we set them to non-zero values explicitly to make sure that in not clipped mode,
@@ -1927,7 +1927,7 @@
 
     @Test
     public void notificationBoundsTopGetsPassedToKeyguard() {
-        mScrimController.transitionTo(SHADE_LOCKED);
+        mScrimController.legacyTransitionTo(SHADE_LOCKED);
         mScrimController.setQsPosition(1f, 0);
         finishAnimationsImmediately();
 
@@ -1938,7 +1938,7 @@
     @Test
     public void notificationBoundsTopDoesNotGetPassedToKeyguardWhenNotifScrimIsNotVisible() {
         mScrimController.setKeyguardOccluded(true);
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
         finishAnimationsImmediately();
 
         mScrimController.setNotificationsBounds(0f, 100f, 0f, 0f);
@@ -1949,7 +1949,7 @@
     public void transitionToDreaming() {
         mScrimController.setRawPanelExpansionFraction(0f);
         mScrimController.setBouncerHiddenFraction(KeyguardBouncerConstants.EXPANSION_HIDDEN);
-        mScrimController.transitionTo(ScrimState.DREAMING);
+        mScrimController.legacyTransitionTo(ScrimState.DREAMING);
         finishAnimationsImmediately();
 
         assertScrimAlpha(Map.of(
@@ -1975,7 +1975,7 @@
 
     @Test
     public void setUnOccludingAnimationKeyguard() {
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
         finishAnimationsImmediately();
         assertThat(mNotificationsScrim.getViewAlpha())
                 .isWithin(0.01f).of(ScrimState.KEYGUARD.getNotifAlpha());
@@ -1990,14 +1990,14 @@
     @Test
     public void testHidesScrimFlickerInActivity() {
         mScrimController.setKeyguardOccluded(true);
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
         finishAnimationsImmediately();
         assertScrimAlpha(Map.of(
                 mScrimInFront, TRANSPARENT,
                 mScrimBehind, TRANSPARENT,
                 mNotificationsScrim, TRANSPARENT));
 
-        mScrimController.transitionTo(SHADE_LOCKED);
+        mScrimController.legacyTransitionTo(SHADE_LOCKED);
         finishAnimationsImmediately();
         assertScrimAlpha(Map.of(
                 mScrimInFront, TRANSPARENT,
@@ -2008,7 +2008,7 @@
     @Test
     public void notificationAlpha_inKeyguardState_bouncerNotActive_clipsQsScrimFalse() {
         mScrimController.setClipsQsScrim(false);
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
 
         float expansion = 0.8f;
         assertAlphaAfterExpansion(mNotificationsScrim, 0f, expansion);
@@ -2016,14 +2016,14 @@
 
     @Test
     public void aodStateSetsFrontScrimToNotBlend() {
-        mScrimController.transitionTo(ScrimState.AOD);
+        mScrimController.legacyTransitionTo(ScrimState.AOD);
         assertFalse("Front scrim should not blend with main color",
                 mScrimInFront.shouldBlendWithMainColor());
     }
 
     @Test
     public void applyState_unlocked_bouncerShowing() {
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
         mScrimController.setBouncerHiddenFraction(0.99f);
         mScrimController.setRawPanelExpansionFraction(0f);
         finishAnimationsImmediately();
@@ -2032,13 +2032,13 @@
 
     @Test
     public void ignoreTransitionRequestWhileKeyguardTransitionRunning() {
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
         mScrimController.mBouncerToGoneTransition.accept(
                 new TransitionStep(KeyguardState.PRIMARY_BOUNCER, KeyguardState.GONE, 0f,
                         TransitionState.RUNNING, "ScrimControllerTest"));
 
         // This request should not happen
-        mScrimController.transitionTo(ScrimState.BOUNCER);
+        mScrimController.legacyTransitionTo(ScrimState.BOUNCER);
         assertThat(mScrimController.getState()).isEqualTo(ScrimState.UNLOCKED);
     }
 
@@ -2055,16 +2055,16 @@
     @Test
     public void testDoNotAnimateChangeIfOccludeAnimationPlaying() {
         mScrimController.setOccludeAnimationPlaying(true);
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
 
         assertFalse(ScrimState.UNLOCKED.mAnimateChange);
     }
 
     @Test
     public void testNotifScrimAlpha_1f_afterUnlockFinishedAndExpanded() {
-        mScrimController.transitionTo(ScrimState.KEYGUARD);
+        mScrimController.legacyTransitionTo(ScrimState.KEYGUARD);
         when(mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()).thenReturn(true);
-        mScrimController.transitionTo(ScrimState.UNLOCKED);
+        mScrimController.legacyTransitionTo(ScrimState.UNLOCKED);
         mScrimController.onUnlockAnimationFinished();
         assertAlphaAfterExpansion(mNotificationsScrim, 1f, 1f);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt
index 87d813c..e0f1e1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLoggerTest.kt
@@ -63,7 +63,7 @@
             StatusBarVisibilityModel(
                 showClock = false,
                 showNotificationIcons = true,
-                showOngoingCallChip = false,
+                showOngoingActivityChip = false,
                 showSystemInfo = true,
             )
         )
@@ -74,7 +74,7 @@
 
         assertThat(actualString).contains("showClock=false")
         assertThat(actualString).contains("showNotificationIcons=true")
-        assertThat(actualString).contains("showOngoingCallChip=false")
+        assertThat(actualString).contains("showOngoingActivityChip=false")
         assertThat(actualString).contains("showSystemInfo=true")
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index ff182ad..ee27cea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -16,6 +16,7 @@
 
 import static android.view.Display.DEFAULT_DISPLAY;
 
+import static com.android.systemui.Flags.FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS;
 import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_CLOSED;
 import static com.android.systemui.shade.ShadeExpansionStateManagerKt.STATE_OPEN;
 
@@ -33,6 +34,8 @@
 import android.content.Context;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
@@ -46,7 +49,6 @@
 import com.android.systemui.animation.AnimatorTestRule;
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.log.LogBuffer;
 import com.android.systemui.log.LogcatEchoTracker;
 import com.android.systemui.plugins.DarkIconDispatcher;
@@ -56,7 +58,6 @@
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.OperatorNameViewController;
-import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModel;
 import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder;
@@ -92,11 +93,9 @@
 @RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
-    private final KosmosJavaAdapter mKosmos = new KosmosJavaAdapter();
     private NotificationIconAreaController mMockNotificationAreaController;
     private ShadeExpansionStateManager mShadeExpansionStateManager;
     private OngoingCallController mOngoingCallController;
-    private OngoingActivityChipsViewModel mOngoingActivityChipsViewModel;
     private SystemStatusAnimationScheduler mAnimationScheduler;
     private StatusBarLocationPublisher mLocationPublisher;
     // Set in instantiate()
@@ -421,6 +420,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
     public void disable_noOngoingCall_chipHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -433,6 +433,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
     public void disable_hasOngoingCall_chipDisplayedAndNotificationIconsHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -446,6 +447,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
     public void disable_hasOngoingCallButNotificationIconsDisabled_chipHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -459,6 +461,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
     public void disable_hasOngoingCallButAlsoHun_chipHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -472,6 +475,7 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
     public void disable_ongoingCallEnded_chipHidden() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
 
@@ -498,8 +502,11 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
     public void disable_hasOngoingCall_hidesNotifsWithoutAnimation() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+        // Enable animations for testing so that we can verify we still aren't animating
+        fragment.enableAnimationsForTesting();
         fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
 
         // Ongoing call started
@@ -512,6 +519,161 @@
     }
 
     @Test
+    @DisableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    public void screenSharingChipsDisabled_ignoresNewCallback() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+
+        // WHEN there *is* an ongoing call via old callback
+        when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, true);
+
+        // WHEN there's *no* ongoing activity via new callback
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasOngoingActivity= */ false);
+
+        // THEN the old callback value is used, so the view is shown
+        assertEquals(View.VISIBLE,
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+
+        // WHEN there's *no* ongoing call via old callback
+        when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+        // WHEN there *is* an ongoing activity via new callback
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasOngoingActivity= */ true);
+
+        // THEN the old callback value is used, so the view is hidden
+        assertEquals(View.GONE,
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+    }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    public void noOngoingActivity_chipHidden() {
+        resumeAndGetFragment();
+
+        // TODO(b/332662551): We *should* be able to just set a value on
+        // mCollapsedStatusBarViewModel.getOngoingActivityChip() instead of manually invoking the
+        // listener, but I'm unable to get the fragment to get attached so that the binder starts
+        // listening to flows.
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasOngoingActivity= */ false);
+
+        assertEquals(View.GONE,
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+    }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    public void hasOngoingActivity_chipDisplayedAndNotificationIconsHidden() {
+        resumeAndGetFragment();
+
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasOngoingActivity= */ true);
+
+        assertEquals(View.VISIBLE,
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+        assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
+    }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    public void hasOngoingActivityButNotificationIconsDisabled_chipHidden() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasOngoingActivity= */ true);
+
+        fragment.disable(DEFAULT_DISPLAY,
+                StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
+
+        assertEquals(View.GONE,
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+    }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    public void hasOngoingActivityButAlsoHun_chipHidden() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasOngoingActivity= */ true);
+        when(mHeadsUpAppearanceController.shouldBeVisible()).thenReturn(true);
+
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+        assertEquals(View.GONE,
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+    }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    public void ongoingActivityEnded_chipHidden() {
+        resumeAndGetFragment();
+
+        // Ongoing activity started
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasOngoingActivity= */ true);
+
+        assertEquals(View.VISIBLE,
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+
+        // Ongoing activity ended
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasOngoingActivity= */ false);
+
+        assertEquals(View.GONE,
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+    }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    public void hasOngoingActivity_hidesNotifsWithoutAnimation() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+        // Enable animations for testing so that we can verify we still aren't animating
+        fragment.enableAnimationsForTesting();
+
+        // Ongoing call started
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasOngoingActivity= */ true);
+
+        // Notification area is hidden without delay
+        assertEquals(0f, getNotificationAreaView().getAlpha(), 0.01);
+        assertEquals(View.INVISIBLE, getNotificationAreaView().getVisibility());
+    }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SCREEN_SHARING_CHIPS)
+    public void screenSharingChipsEnabled_ignoresOngoingCallController() {
+        CollapsedStatusBarFragment fragment = resumeAndGetFragment();
+
+        // WHEN there *is* an ongoing call via old callback
+        when(mOngoingCallController.hasOngoingCall()).thenReturn(true);
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, true);
+
+        // WHEN there's *no* ongoing activity via new callback
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasOngoingActivity= */ false);
+
+        // THEN the new callback value is used, so the view is hidden
+        assertEquals(View.GONE,
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+
+        // WHEN there's *no* ongoing call via old callback
+        when(mOngoingCallController.hasOngoingCall()).thenReturn(false);
+        fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
+
+        // WHEN there *is* an ongoing activity via new callback
+        mCollapsedStatusBarViewBinder.getListener().onOngoingActivityStatusChanged(
+                /* hasOngoingActivity= */ true);
+
+        // THEN the new callback value is used, so the view is shown
+        assertEquals(View.VISIBLE,
+                mFragment.getView().findViewById(R.id.ongoing_activity_chip).getVisibility());
+    }
+
+    @Test
     public void disable_isDozing_clockAndSystemInfoVisible() {
         CollapsedStatusBarFragment fragment = resumeAndGetFragment();
         when(mStatusBarStateController.isDozing()).thenReturn(true);
@@ -670,7 +832,6 @@
         MockitoAnnotations.initMocks(this);
         setUpDaggerComponent();
         mOngoingCallController = mock(OngoingCallController.class);
-        mOngoingActivityChipsViewModel = mKosmos.getOngoingActivityChipsViewModel();
         mAnimationScheduler = mock(SystemStatusAnimationScheduler.class);
         mLocationPublisher = mock(StatusBarLocationPublisher.class);
         mStatusBarIconController = mock(StatusBarIconController.class);
@@ -691,7 +852,6 @@
         return new CollapsedStatusBarFragment(
                 mStatusBarFragmentComponentFactory,
                 mOngoingCallController,
-                mOngoingActivityChipsViewModel,
                 mAnimationScheduler,
                 mLocationPublisher,
                 mMockNotificationAreaController,
@@ -773,7 +933,9 @@
     private CollapsedStatusBarFragment resumeAndGetFragment() {
         mFragments.dispatchResume();
         processAllMessages();
-        return (CollapsedStatusBarFragment) mFragment;
+        CollapsedStatusBarFragment fragment = (CollapsedStatusBarFragment) mFragment;
+        fragment.disableAnimationsForTesting();
+        return fragment;
     }
 
     private View getUserChipView() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt
index 8e789cb..022b5d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/StatusBarVisibilityModelTest.kt
@@ -36,7 +36,7 @@
             StatusBarVisibilityModel(
                 showClock = true,
                 showNotificationIcons = true,
-                showOngoingCallChip = true,
+                showOngoingActivityChip = true,
                 showSystemInfo = true,
             )
 
@@ -72,17 +72,17 @@
     }
 
     @Test
-    fun createModelFromFlags_ongoingCallChipNotDisabled_showOngoingCallChipTrue() {
+    fun createModelFromFlags_ongoingCallChipNotDisabled_showOngoingActivityChipTrue() {
         val result = createModelFromFlags(disabled1 = 0, disabled2 = 0)
 
-        assertThat(result.showOngoingCallChip).isTrue()
+        assertThat(result.showOngoingActivityChip).isTrue()
     }
 
     @Test
-    fun createModelFromFlags_ongoingCallChipDisabled_showOngoingCallChipFalse() {
+    fun createModelFromFlags_ongoingCallChipDisabled_showOngoingActivityChipFalse() {
         val result = createModelFromFlags(disabled1 = DISABLE_ONGOING_CALL_CHIP, disabled2 = 0)
 
-        assertThat(result.showOngoingCallChip).isFalse()
+        assertThat(result.showOngoingActivityChip).isFalse()
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
index eb2538e..7d586cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileRepositorySwitcherTest.kt
@@ -131,8 +131,9 @@
                 mobileMappings,
                 fakeBroadcastDispatcher,
                 context,
-                IMMEDIATE,
+                /* bgDispatcher = */ IMMEDIATE,
                 scope,
+                /* mainDispatcher = */ IMMEDIATE,
                 FakeAirplaneModeRepository(),
                 wifiRepository,
                 mock(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index 96e599f..76982ae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -141,8 +141,8 @@
     private val wifiPickerTrackerCallback =
         argumentCaptor<WifiPickerTracker.WifiPickerTrackerCallback>()
 
-    private val dispatcher = StandardTestDispatcher()
-    private val testScope = TestScope(dispatcher)
+    private val testDispatcher = StandardTestDispatcher()
+    private val testScope = TestScope(testDispatcher)
 
     private lateinit var underTest: MobileConnectionsRepositoryImpl
 
@@ -194,7 +194,7 @@
                 flags,
                 testScope.backgroundScope,
                 mainExecutor,
-                dispatcher,
+                testDispatcher,
                 wifiPickerTrackerFactory,
                 wifiManager,
                 wifiLogBuffer,
@@ -216,7 +216,7 @@
                 fakeBroadcastDispatcher,
                 connectivityManager,
                 telephonyManager = telephonyManager,
-                bgDispatcher = dispatcher,
+                bgDispatcher = testDispatcher,
                 logger = logger,
                 mobileMappingsProxy = mobileMappings,
                 scope = testScope.backgroundScope,
@@ -249,8 +249,9 @@
                 mobileMappings,
                 fakeBroadcastDispatcher,
                 context,
-                dispatcher,
+                /* bgDispatcher = */ testDispatcher,
                 testScope.backgroundScope,
+                /* mainDispatcher = */ testDispatcher,
                 airplaneModeRepository,
                 wifiRepository,
                 fullConnectionFactory,
@@ -1225,8 +1226,9 @@
                     mobileMappings,
                     fakeBroadcastDispatcher,
                     context,
-                    dispatcher,
+                    testDispatcher,
                     testScope.backgroundScope,
+                    testDispatcher,
                     airplaneModeRepository,
                     wifiRepository,
                     fullConnectionFactory,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt
index 6300953..cdc4733 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/DeviceBasedSatelliteRepositorySwitcherTest.kt
@@ -60,7 +60,8 @@
             telephonyManager,
             testDispatcher,
             testScope.backgroundScope,
-            FakeLogBuffer.Factory.create(),
+            logBuffer = FakeLogBuffer.Factory.create(),
+            verboseLogBuffer = FakeLogBuffer.Factory.create(),
             systemClock,
         )
     private val demoDataSource =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
index 6651676..d24d87c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
@@ -34,6 +34,7 @@
 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_UNKNOWN
 import android.telephony.satellite.SatelliteManager.SatelliteException
 import android.telephony.satellite.SatelliteModemStateCallback
+import android.telephony.satellite.SatelliteSupportedStateCallback
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
@@ -94,7 +95,8 @@
                     telephonyManager,
                     dispatcher,
                     testScope.backgroundScope,
-                    FakeLogBuffer.Factory.create(),
+                    logBuffer = FakeLogBuffer.Factory.create(),
+                    verboseLogBuffer = FakeLogBuffer.Factory.create(),
                     systemClock,
                 )
 
@@ -326,7 +328,6 @@
     @Test
     fun satelliteNotSupported_listenersAreNotRegistered() =
         testScope.runTest {
-            setupDefaultRepo()
             // GIVEN satellite is not supported
             setUpRepo(
                 uptime = MIN_UPTIME,
@@ -344,6 +345,110 @@
         }
 
     @Test
+    fun satelliteSupported_registersCallbackForStateChanges() =
+        testScope.runTest {
+            // GIVEN a supported satellite manager.
+            setupDefaultRepo()
+            runCurrent()
+
+            // THEN the repo registers for state changes of satellite support
+            verify(satelliteManager, times(1)).registerForSupportedStateChanged(any(), any())
+        }
+
+    @Test
+    fun satelliteNotSupported_registersCallbackForStateChanges() =
+        testScope.runTest {
+            // GIVEN satellite is not supported
+            setUpRepo(
+                uptime = MIN_UPTIME,
+                satMan = satelliteManager,
+                satelliteSupported = false,
+            )
+
+            runCurrent()
+            // THEN the repo registers for state changes of satellite support
+            verify(satelliteManager, times(1)).registerForSupportedStateChanged(any(), any())
+        }
+
+    @Test
+    fun satelliteSupportedStateChangedCallbackThrows_doesNotCrash() =
+        testScope.runTest {
+            // GIVEN, satellite manager throws when registering for supported state changes
+            whenever(satelliteManager.registerForSupportedStateChanged(any(), any()))
+                .thenThrow(IllegalStateException())
+
+            // GIVEN a supported satellite manager.
+            setupDefaultRepo()
+            runCurrent()
+
+            // THEN a listener for satellite supported changed can attempt to register,
+            // with no crash
+            verify(satelliteManager).registerForSupportedStateChanged(any(), any())
+        }
+
+    @Test
+    fun satelliteSupported_supportIsLost_unregistersListeners() =
+        testScope.runTest {
+            // GIVEN a supported satellite manager.
+            setupDefaultRepo()
+            runCurrent()
+
+            val callback =
+                withArgCaptor<SatelliteSupportedStateCallback> {
+                    verify(satelliteManager).registerForSupportedStateChanged(any(), capture())
+                }
+
+            // WHEN data is requested from the repo
+            val connectionState by collectLastValue(underTest.connectionState)
+            val signalStrength by collectLastValue(underTest.signalStrength)
+
+            // THEN the listeners are registered
+            verify(satelliteManager, times(1)).registerForModemStateChanged(any(), any())
+            verify(satelliteManager, times(1)).registerForNtnSignalStrengthChanged(any(), any())
+
+            // WHEN satellite support turns off
+            callback.onSatelliteSupportedStateChanged(false)
+            runCurrent()
+
+            // THEN listeners are unregistered
+            verify(satelliteManager, times(1)).unregisterForModemStateChanged(any())
+            verify(satelliteManager, times(1)).unregisterForNtnSignalStrengthChanged(any())
+        }
+
+    @Test
+    fun satelliteNotSupported_supportShowsUp_registersListeners() =
+        testScope.runTest {
+            // GIVEN satellite is not supported
+            setUpRepo(
+                uptime = MIN_UPTIME,
+                satMan = satelliteManager,
+                satelliteSupported = false,
+            )
+            runCurrent()
+
+            val callback =
+                withArgCaptor<SatelliteSupportedStateCallback> {
+                    verify(satelliteManager).registerForSupportedStateChanged(any(), capture())
+                }
+
+            // WHEN data is requested from the repo
+            val connectionState by collectLastValue(underTest.connectionState)
+            val signalStrength by collectLastValue(underTest.signalStrength)
+
+            // THEN the listeners are not yet registered
+            verify(satelliteManager, times(0)).registerForModemStateChanged(any(), any())
+            verify(satelliteManager, times(0)).registerForNtnSignalStrengthChanged(any(), any())
+
+            // WHEN satellite support turns on
+            callback.onSatelliteSupportedStateChanged(true)
+            runCurrent()
+
+            // THEN listeners are registered
+            verify(satelliteManager, times(1)).registerForModemStateChanged(any(), any())
+            verify(satelliteManager, times(1)).registerForNtnSignalStrengthChanged(any(), any())
+        }
+
+    @Test
     fun repoDoesNotCheckForSupportUntilMinUptime() =
         testScope.runTest {
             // GIVEN we init 100ms after sysui starts up
@@ -451,7 +556,8 @@
                 telephonyManager,
                 dispatcher,
                 testScope.backgroundScope,
-                FakeLogBuffer.Factory.create(),
+                logBuffer = FakeLogBuffer.Factory.create(),
+                verboseLogBuffer = FakeLogBuffer.Factory.create(),
                 systemClock,
             )
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
index 606feab..c9fe449 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
@@ -32,6 +32,14 @@
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.log.assertLogsWtf
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
+import com.android.systemui.screenrecord.data.model.ScreenRecordModel
+import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsMediaProjectionChip
+import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsScreenRecordChip
+import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel
 import com.android.systemui.statusbar.data.model.StatusBarMode
 import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository.Companion.DISPLAY_ID
 import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
@@ -65,6 +73,7 @@
             kosmos.lightsOutInteractor,
             kosmos.activeNotificationsInteractor,
             kosmos.keyguardTransitionInteractor,
+            kosmos.ongoingActivityChipsViewModel,
             kosmos.applicationCoroutineScope,
         )
 
@@ -382,6 +391,25 @@
             }
         }
 
+    @Test
+    fun ongoingActivityChip_matchesViewModel() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.ongoingActivityChip)
+
+            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording
+
+            assertIsScreenRecordChip(latest)
+
+            kosmos.screenRecordRepository.screenRecordState.value = ScreenRecordModel.DoingNothing
+
+            assertThat(latest).isEqualTo(OngoingActivityChipModel.Hidden)
+
+            kosmos.fakeMediaProjectionRepository.mediaProjectionState.value =
+                MediaProjectionState.EntireScreen
+
+            assertIsMediaProjectionChip(latest)
+        }
+
     private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) =
         ActiveNotificationsStore.Builder()
             .apply { notifications.forEach(::addIndividualNotif) }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
index bc50f79..c3c9907 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
 
+import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
@@ -27,6 +28,9 @@
 
     override val transitionFromLockscreenToDreamStartedEvent = MutableSharedFlow<Unit>()
 
+    override val ongoingActivityChip: MutableStateFlow<OngoingActivityChipModel> =
+        MutableStateFlow(OngoingActivityChipModel.Hidden)
+
     override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> = areNotificationLightsOut
 
     fun setNotificationLightsOut(lightsOut: Boolean) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxEffectTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxEffectTest.kt
new file mode 100644
index 0000000..16132ba
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/glowboxeffect/GlowBoxEffectTest.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.surfaceeffects.glowboxeffect
+
+import android.graphics.Color
+import android.graphics.Paint
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.AnimatorTestRule
+import com.android.systemui.surfaceeffects.PaintDrawCallback
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class GlowBoxEffectTest : SysuiTestCase() {
+
+    @get:Rule val animatorTestRule = AnimatorTestRule(this)
+    private lateinit var config: GlowBoxConfig
+    private lateinit var glowBoxEffect: GlowBoxEffect
+    private lateinit var drawCallback: PaintDrawCallback
+
+    @Before
+    fun setup() {
+        drawCallback =
+            object : PaintDrawCallback {
+                override fun onDraw(paint: Paint) {}
+            }
+        config =
+            GlowBoxConfig(
+                startCenterX = 0f,
+                startCenterY = 0f,
+                endCenterX = 0f,
+                endCenterY = 0f,
+                width = 1f,
+                height = 1f,
+                color = Color.WHITE,
+                blurAmount = 0.1f,
+                duration = 100L,
+                easeInDuration = 100L,
+                easeOutDuration = 100L
+            )
+        glowBoxEffect = GlowBoxEffect(config, drawCallback)
+    }
+
+    @Test
+    fun play_paintCallback_triggersDrawCallback() {
+        var paintFromCallback: Paint? = null
+        drawCallback =
+            object : PaintDrawCallback {
+                override fun onDraw(paint: Paint) {
+                    paintFromCallback = paint
+                }
+            }
+        glowBoxEffect = GlowBoxEffect(config, drawCallback)
+
+        assertThat(paintFromCallback).isNull()
+
+        glowBoxEffect.play()
+        animatorTestRule.advanceTimeBy(50L)
+
+        assertThat(paintFromCallback).isNotNull()
+    }
+
+    @Test
+    fun play_followsAnimationStateInOrder() {
+        assertThat(glowBoxEffect.state).isEqualTo(GlowBoxEffect.AnimationState.NOT_PLAYING)
+
+        glowBoxEffect.play()
+
+        assertThat(glowBoxEffect.state).isEqualTo(GlowBoxEffect.AnimationState.EASE_IN)
+
+        animatorTestRule.advanceTimeBy(config.easeInDuration + 50L)
+
+        assertThat(glowBoxEffect.state).isEqualTo(GlowBoxEffect.AnimationState.MAIN)
+
+        animatorTestRule.advanceTimeBy(config.duration + 50L)
+
+        assertThat(glowBoxEffect.state).isEqualTo(GlowBoxEffect.AnimationState.EASE_OUT)
+
+        animatorTestRule.advanceTimeBy(config.easeOutDuration + 50L)
+
+        assertThat(glowBoxEffect.state).isEqualTo(GlowBoxEffect.AnimationState.NOT_PLAYING)
+    }
+
+    @Test
+    fun finish_statePlaying_finishesAnimation() {
+        assertThat(glowBoxEffect.state).isEqualTo(GlowBoxEffect.AnimationState.NOT_PLAYING)
+
+        glowBoxEffect.play()
+        glowBoxEffect.finish()
+
+        assertThat(glowBoxEffect.state).isEqualTo(GlowBoxEffect.AnimationState.EASE_OUT)
+    }
+
+    @Test
+    fun finish_stateNotPlaying_doesNotFinishAnimation() {
+        assertThat(glowBoxEffect.state).isEqualTo(GlowBoxEffect.AnimationState.NOT_PLAYING)
+
+        glowBoxEffect.finish()
+
+        assertThat(glowBoxEffect.state).isEqualTo(GlowBoxEffect.AnimationState.NOT_PLAYING)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt
index 6f58941..41d7fd5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/surfaceeffects/loadingeffect/LoadingEffectTest.kt
@@ -21,8 +21,8 @@
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.AnimatorTestRule
-import com.android.systemui.model.SysUiStateTest
 import com.android.systemui.surfaceeffects.PaintDrawCallback
 import com.android.systemui.surfaceeffects.RenderEffectDrawCallback
 import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseAnimationConfig
@@ -35,7 +35,7 @@
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
-class LoadingEffectTest : SysUiStateTest() {
+class LoadingEffectTest : SysuiTestCase() {
 
     @get:Rule val animatorTestRule = AnimatorTestRule(this)
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
index 1466d24..664f2df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/chipbar/ChipbarCoordinatorTest.kt
@@ -19,7 +19,6 @@
 import android.os.PowerManager
 import android.os.VibrationAttributes
 import android.os.VibrationEffect
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import android.view.MotionEvent
 import android.view.View
@@ -29,6 +28,7 @@
 import android.widget.ImageView
 import android.widget.TextView
 import androidx.core.animation.doOnCancel
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.InstanceId
 import com.android.internal.logging.testing.UiEventLoggerFake
@@ -68,7 +68,7 @@
 import org.mockito.MockitoAnnotations
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper
 class ChipbarCoordinatorTest : SysuiTestCase() {
     private lateinit var underTest: ChipbarCoordinator
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
index 8f4cbaf..31ee858 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayApplierTest.java
@@ -45,9 +45,9 @@
 import android.content.om.OverlayManager;
 import android.content.om.OverlayManagerTransaction;
 import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -69,7 +69,7 @@
 import java.util.Set;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class ThemeOverlayApplierTest extends SysuiTestCase {
     private static final String TEST_DISABLED_PREFIX = "com.example.";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 5ad88ad..53e033e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -52,9 +52,9 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
 
 import androidx.annotation.VisibleForTesting;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -88,7 +88,7 @@
 import java.util.concurrent.Executor;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class ThemeOverlayControllerTest extends SysuiTestCase {
 
     private static final int USER_SYSTEM = UserHandle.USER_SYSTEM;
@@ -202,7 +202,7 @@
         verify(mWakefulnessLifecycle).addObserver(mWakefulnessLifecycleObserver.capture());
         verify(mDumpManager).registerDumpable(any(), any());
         verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture());
-        verify(mSecureSettings).registerContentObserverForUser(
+        verify(mSecureSettings).registerContentObserverForUserSync(
                 eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES),
                 eq(false), mSettingsObserver.capture(), eq(UserHandle.USER_ALL)
         );
diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
index 0581e0e..8df37ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
@@ -46,7 +46,6 @@
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.Display;
 import android.view.LayoutInflater;
@@ -59,6 +58,7 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.util.IntPair;
@@ -80,7 +80,7 @@
 import java.util.Arrays;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class ToastUITest extends SysuiTestCase {
     private static final int ANDROID_UID = 1000;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touch/TouchInsetManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/touch/TouchInsetManagerTest.java
index eb932d2..ce5899a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/touch/TouchInsetManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/touch/TouchInsetManagerTest.java
@@ -25,11 +25,11 @@
 
 import android.graphics.Rect;
 import android.graphics.Region;
-import android.testing.AndroidTestingRunner;
 import android.view.AttachedSurfaceControl;
 import android.view.View;
 import android.view.ViewGroup;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -45,7 +45,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 public class TouchInsetManagerTest extends SysuiTestCase {
     @Mock
     private AttachedSurfaceControl mAttachedSurfaceControl;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/tracing/TraceUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/tracing/TraceUtilsTest.kt
index bda339f..eef4e3b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/tracing/TraceUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/tracing/TraceUtilsTest.kt
@@ -17,8 +17,8 @@
 import android.os.Handler
 import android.os.Looper
 import android.os.Trace.TRACE_TAG_APP
-import android.testing.AndroidTestingRunner
 import android.util.Log
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.app.tracing.TraceUtils.traceRunnable
 import com.android.app.tracing.namedRunnable
@@ -30,7 +30,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class TraceUtilsTest : SysuiTestCase() {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
index 99f6303..25a44e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
@@ -83,7 +83,7 @@
 
     @Test
     public void testRegisterContentObserver() {
-        mFakeSettings.registerContentObserver("cat", mContentObserver);
+        mFakeSettings.registerContentObserverSync("cat", mContentObserver);
 
         mFakeSettings.putString("cat", "hat");
 
@@ -93,7 +93,7 @@
 
     @Test
     public void testRegisterContentObserverAllUsers() {
-        mFakeSettings.registerContentObserverForUser(
+        mFakeSettings.registerContentObserverForUserSync(
                 mFakeSettings.getUriFor("cat"), false, mContentObserver, UserHandle.USER_ALL);
 
         mFakeSettings.putString("cat", "hat");
@@ -104,8 +104,8 @@
 
     @Test
     public void testUnregisterContentObserver() {
-        mFakeSettings.registerContentObserver("cat", mContentObserver);
-        mFakeSettings.unregisterContentObserver(mContentObserver);
+        mFakeSettings.registerContentObserverSync("cat", mContentObserver);
+        mFakeSettings.unregisterContentObserverSync(mContentObserver);
 
         mFakeSettings.putString("cat", "hat");
 
@@ -115,9 +115,9 @@
 
     @Test
     public void testUnregisterContentObserverAllUsers() {
-        mFakeSettings.registerContentObserverForUser(
+        mFakeSettings.registerContentObserverForUserSync(
                 mFakeSettings.getUriFor("cat"), false, mContentObserver, UserHandle.USER_ALL);
-        mFakeSettings.unregisterContentObserver(mContentObserver);
+        mFakeSettings.unregisterContentObserverSync(mContentObserver);
 
         mFakeSettings.putString("cat", "hat");
 
@@ -128,7 +128,7 @@
     @Test
     public void testContentObserverDispatchCorrectUser() {
         int user = 10;
-        mFakeSettings.registerContentObserverForUser(
+        mFakeSettings.registerContentObserverForUserSync(
                 mFakeSettings.getUriFor("cat"), false, mContentObserver, UserHandle.USER_ALL
         );
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
index ab95707..eb11e38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
@@ -52,14 +52,14 @@
 
     @Test
     fun registerContentObserver_inputString_success() {
-        mSettings.registerContentObserver(TEST_SETTING, mContentObserver)
+        mSettings.registerContentObserverSync(TEST_SETTING, mContentObserver)
         verify(mSettings.getContentResolver())
             .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver))
     }
 
     @Test
     fun registerContentObserver_inputString_notifyForDescendants_true() {
-        mSettings.registerContentObserver(
+        mSettings.registerContentObserverSync(
             TEST_SETTING,
             notifyForDescendants = true,
             mContentObserver
@@ -70,14 +70,14 @@
 
     @Test
     fun registerContentObserver_inputUri_success() {
-        mSettings.registerContentObserver(TEST_SETTING_URI, mContentObserver)
+        mSettings.registerContentObserverSync(TEST_SETTING_URI, mContentObserver)
         verify(mSettings.getContentResolver())
             .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver))
     }
 
     @Test
     fun registerContentObserver_inputUri_notifyForDescendants_true() {
-        mSettings.registerContentObserver(
+        mSettings.registerContentObserverSync(
             TEST_SETTING_URI,
             notifyForDescendants = true,
             mContentObserver
@@ -88,7 +88,7 @@
 
     @Test
     fun unregisterContentObserver() {
-        mSettings.unregisterContentObserver(mContentObserver)
+        mSettings.unregisterContentObserverSync(mContentObserver)
         verify(mSettings.getContentResolver()).unregisterContentObserver(eq(mContentObserver))
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
index 56328b9..38469ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
@@ -58,7 +58,7 @@
 
     @Test
     fun registerContentObserverForUser_inputString_success() {
-        mSettings.registerContentObserverForUser(
+        mSettings.registerContentObserverForUserSync(
             TEST_SETTING,
             mContentObserver,
             mUserTracker.userId
@@ -74,7 +74,7 @@
 
     @Test
     fun registerContentObserverForUser_inputString_notifyForDescendants_true() {
-        mSettings.registerContentObserverForUser(
+        mSettings.registerContentObserverForUserSync(
             TEST_SETTING,
             notifyForDescendants = true,
             mContentObserver,
@@ -91,7 +91,7 @@
 
     @Test
     fun registerContentObserverForUser_inputUri_success() {
-        mSettings.registerContentObserverForUser(
+        mSettings.registerContentObserverForUserSync(
             TEST_SETTING_URI,
             mContentObserver,
             mUserTracker.userId
@@ -107,7 +107,7 @@
 
     @Test
     fun registerContentObserverForUser_inputUri_notifyForDescendants_true() {
-        mSettings.registerContentObserverForUser(
+        mSettings.registerContentObserverForUserSync(
             TEST_SETTING_URI,
             notifyForDescendants = true,
             mContentObserver,
@@ -124,14 +124,14 @@
 
     @Test
     fun registerContentObserver_inputUri_success() {
-        mSettings.registerContentObserver(TEST_SETTING_URI, mContentObserver)
+        mSettings.registerContentObserverSync(TEST_SETTING_URI, mContentObserver)
         verify(mSettings.getContentResolver())
             .registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver), eq(0))
     }
 
     @Test
     fun registerContentObserver_inputUri_notifyForDescendants_true() {
-        mSettings.registerContentObserver(
+        mSettings.registerContentObserverSync(
             TEST_SETTING_URI,
             notifyForDescendants = true,
             mContentObserver
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/CsdWarningDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/CsdWarningDialogTest.java
index 741b2e2..c81623e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/CsdWarningDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/CsdWarningDialogTest.java
@@ -27,9 +27,9 @@
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.media.AudioManager;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.messages.nano.SystemMessageProto;
@@ -42,7 +42,7 @@
 import org.junit.runner.RunWith;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class CsdWarningDialogTest extends SysuiTestCase {
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index 69d7586..f737148 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -37,10 +37,10 @@
 import android.media.session.MediaSession;
 import android.os.Handler;
 import android.os.Process;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.accessibility.AccessibilityManager;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -65,7 +65,7 @@
 
 import java.util.concurrent.Executor;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @SmallTest
 @TestableLooper.RunWithLooper
 public class VolumeDialogControllerImplTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 9864439..05d07e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -52,7 +52,6 @@
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
 import android.provider.Settings;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.Log;
 import android.view.Gravity;
@@ -65,6 +64,7 @@
 import android.widget.SeekBar;
 
 import androidx.test.core.view.MotionEventBuilder;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.jank.InteractionJankMonitor;
@@ -107,7 +107,7 @@
 import java.util.function.Predicate;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class VolumeDialogImplTest extends SysuiTestCase {
     VolumeDialogImpl mDialog;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
index dc5597a..40094e5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
@@ -33,9 +33,9 @@
 import android.content.Intent;
 import android.service.quickaccesswallet.GetWalletCardsRequest;
 import android.service.quickaccesswallet.QuickAccessWalletClient;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -57,7 +57,7 @@
 
 import java.util.List;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 @SmallTest
 public class QuickAccessWalletControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt
index d2387e8..6f99cd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt
@@ -6,6 +6,7 @@
 import android.graphics.drawable.Icon
 import android.os.Looper
 import android.service.quickaccesswallet.WalletCard
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FakeFeatureFlags
@@ -22,7 +23,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.Mock
 import org.mockito.Mockito.anySet
 import org.mockito.Mockito.doNothing
@@ -31,7 +31,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 @kotlinx.coroutines.ExperimentalCoroutinesApi
 class WalletContextualLocationsServiceTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt
index d5bdb59..4e44c4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt
@@ -22,6 +22,7 @@
 import android.service.quickaccesswallet.GetWalletCardsResponse
 import android.service.quickaccesswallet.QuickAccessWalletClient
 import android.service.quickaccesswallet.WalletCard
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
@@ -42,7 +43,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
 import org.mockito.ArgumentCaptor
 import org.mockito.Captor
 import org.mockito.Mock
@@ -53,7 +53,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class WalletContextualSuggestionsControllerTest : SysuiTestCase() {
 
     @Mock private lateinit var walletController: QuickAccessWalletController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
index c1d11aa..38a61fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/ui/WalletScreenControllerTest.java
@@ -42,9 +42,9 @@
 import android.service.quickaccesswallet.QuickAccessWalletClient;
 import android.service.quickaccesswallet.QuickAccessWalletService;
 import android.service.quickaccesswallet.WalletCard;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.UiEventLogger;
@@ -68,7 +68,7 @@
 import java.util.Collections;
 import java.util.List;
 
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 @SmallTest
 public class WalletScreenControllerTest extends SysuiTestCase {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt
index e46c1f5..1df781f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.wallet.util
 
 import android.service.quickaccesswallet.WalletCard
-import android.testing.AndroidTestingRunner
 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.util.mockito.mock
@@ -27,7 +27,7 @@
 import org.junit.runner.RunWith
 
 /** Test class for WalletCardUtils */
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @RunWithLooper
 @SmallTest
 class WalletCardUtilsTest : SysuiTestCase() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
index fc2030f..6fb70de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
@@ -44,13 +44,13 @@
 import android.graphics.ColorSpace;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.view.Surface;
 import android.view.SurfaceHolder;
 import android.view.WindowManager;
 import android.view.WindowMetrics;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -65,7 +65,7 @@
 import org.mockito.MockitoAnnotations;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class ImageWallpaperTest extends SysuiTestCase {
     private static final int LOW_BMP_WIDTH = 128;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java
index 33d09c1..75e027e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java
@@ -36,9 +36,9 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.platform.test.annotations.EnableFlags;
-import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -56,7 +56,7 @@
 import java.util.concurrent.Executor;
 
 @SmallTest
-@RunWith(AndroidTestingRunner.class)
+@RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper
 public class WallpaperLocalColorExtractorTest extends SysuiTestCase {
     private static final int LOW_BMP_WIDTH = 112;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
index 7801684..2021f02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubbleEducationControllerTest.kt
@@ -21,9 +21,9 @@
 import android.content.pm.ShortcutInfo
 import android.content.res.Resources
 import android.os.UserHandle
-import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
 import androidx.core.content.edit
+import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.model.SysUiStateTest
 import com.android.wm.shell.bubbles.Bubble
@@ -38,7 +38,7 @@
 import org.junit.runner.RunWith
 
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class BubbleEducationControllerTest : SysUiStateTest() {
 
diff --git a/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt b/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt
index 1a9f4b4..430fb59 100644
--- a/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt
+++ b/packages/SystemUI/tests/utils/src/android/graphics/drawable/TestStubDrawable.kt
@@ -27,8 +27,11 @@
 class TestStubDrawable(private val name: String? = null) : Drawable() {
 
     override fun draw(canvas: Canvas) = Unit
+
     override fun setAlpha(alpha: Int) = Unit
+
     override fun setColorFilter(colorFilter: ColorFilter?) = Unit
+
     override fun getOpacity(): Int = PixelFormat.UNKNOWN
 
     override fun toString(): String {
@@ -38,6 +41,10 @@
     override fun getConstantState(): ConstantState =
         TestStubConstantState(this, changingConfigurations)
 
+    override fun equals(other: Any?): Boolean {
+        return (other as? TestStubDrawable ?: return false).name == name
+    }
+
     private class TestStubConstantState(
         private val drawable: Drawable,
         private val changingConfigurations: Int,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiBaseFragmentTest.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiBaseFragmentTest.java
index e470406..a581993 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiBaseFragmentTest.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiBaseFragmentTest.java
@@ -19,6 +19,7 @@
 
 import android.app.Fragment;
 import android.app.Instrumentation;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.testing.BaseFragmentTest;
 import android.testing.DexmakerShareClassLoaderRule;
 
@@ -31,6 +32,7 @@
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
+import org.junit.ClassRule;
 import org.junit.Rule;
 import org.mockito.Mockito;
 
@@ -43,6 +45,12 @@
     @Rule
     public final SysuiLeakCheck mLeakCheck = new SysuiLeakCheck();
 
+    @ClassRule
+    public static final SetFlagsRule.ClassRule mSetFlagsClassRule =
+            new SetFlagsRule.ClassRule(
+                    com.android.systemui.Flags.class);
+    @Rule public final SetFlagsRule mSetFlagsRule = mSetFlagsClassRule.createSetFlagsRule();
+
     @Rule
     public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
             new DexmakerShareClassLoaderRule();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt
index 9765d53..53285eb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/data/repository/FakeDisplayStateRepository.kt
@@ -23,7 +23,6 @@
 import dagger.Binds
 import dagger.Module
 import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
@@ -40,7 +39,7 @@
     override val currentDisplaySize: StateFlow<Size> = _currentDisplaySize.asStateFlow()
 
     private val _isLargeScreen = MutableStateFlow<Boolean>(false)
-    override val isLargeScreen: Flow<Boolean> = _isLargeScreen.asStateFlow()
+    override val isLargeScreen: StateFlow<Boolean> = _isLargeScreen.asStateFlow()
 
     override val isReverseDefaultRotation = false
 
@@ -55,6 +54,10 @@
     fun setCurrentDisplaySize(size: Size) {
         _currentDisplaySize.value = size
     }
+
+    fun setIsLargeScreen(isLargeScreen: Boolean) {
+        _isLargeScreen.value = isLargeScreen
+    }
 }
 
 @Module
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorKosmos.kt
index 7f9a71c..56297f0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/PromptSelectorInteractorKosmos.kt
@@ -25,6 +25,7 @@
 val Kosmos.promptSelectorInteractor by Fixture {
     PromptSelectorInteractorImpl(
         fingerprintPropertyRepository = fingerprintPropertyRepository,
+        displayStateInteractor = displayStateInteractor,
         promptRepository = promptRepository,
         lockPatternUtils = lockPatternUtils
     )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeScreenBrightnessRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeScreenBrightnessRepository.kt
index a05b5e6..ad5242e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeScreenBrightnessRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/data/repository/FakeScreenBrightnessRepository.kt
@@ -19,7 +19,7 @@
 import android.hardware.display.BrightnessInfo
 import android.hardware.display.BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE
 import android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF
-import com.android.systemui.brightness.data.model.LinearBrightness
+import com.android.systemui.brightness.shared.model.LinearBrightness
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.map
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorKosmos.kt
index 22784e4..0e84273 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/domain/interactor/ScreenBrightnessInteractorKosmos.kt
@@ -18,6 +18,15 @@
 
 import com.android.systemui.brightness.data.repository.screenBrightnessRepository
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.util.mockito.mock
 
 val Kosmos.screenBrightnessInteractor by
-    Kosmos.Fixture { ScreenBrightnessInteractor(screenBrightnessRepository) }
+    Kosmos.Fixture {
+        ScreenBrightnessInteractor(
+            screenBrightnessRepository,
+            applicationCoroutineScope,
+            mock<TableLogBuffer>(),
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt
new file mode 100644
index 0000000..d208465
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/brightness/ui/viewmodel/BrightnessSliderViewModelKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.brightness.ui.viewmodel
+
+import com.android.systemui.brightness.domain.interactor.brightnessPolicyEnforcementInteractor
+import com.android.systemui.brightness.domain.interactor.screenBrightnessInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+
+val Kosmos.brightnessSliderViewModel: BrightnessSliderViewModel by
+    Kosmos.Fixture {
+        BrightnessSliderViewModel(
+            screenBrightnessInteractor = screenBrightnessInteractor,
+            brightnessPolicyEnforcementInteractor = brightnessPolicyEnforcementInteractor,
+            applicationScope = applicationCoroutineScope,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSceneRepositoryKosmos.kt
similarity index 85%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalRepositoryKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSceneRepositoryKosmos.kt
index c7955c3..a7a18a0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/CommunalSceneRepositoryKosmos.kt
@@ -20,8 +20,9 @@
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.applicationCoroutineScope
 
-val Kosmos.fakeCommunalRepository by Fixture {
+val Kosmos.fakeCommunalSceneRepository by Fixture {
     FakeCommunalSceneRepository(applicationScope = applicationCoroutineScope)
 }
 
-val Kosmos.communalSceneRepository by Fixture<CommunalSceneRepository> { fakeCommunalRepository }
+val Kosmos.communalSceneRepository by
+    Fixture<CommunalSceneRepository> { fakeCommunalSceneRepository }
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 cd2710e..fb983f7 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
@@ -21,6 +21,7 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.kosmos.applicationCoroutineScope
+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
@@ -29,6 +30,7 @@
     CommunalSettingsInteractor(
         bgScope = applicationCoroutineScope,
         bgExecutor = fakeExecutor,
+        bgDispatcher = testDispatcher,
         repository = communalSettingsRepository,
         userInteractor = selectedUserInteractor,
         userTracker = userTracker,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 1a45c42..22b8c8db 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -104,7 +104,8 @@
 
     private val _biometricUnlockState =
         MutableStateFlow(BiometricUnlockModel(BiometricUnlockMode.NONE, null))
-    override val biometricUnlockState: Flow<BiometricUnlockModel> = _biometricUnlockState
+    override val biometricUnlockState: StateFlow<BiometricUnlockModel> =
+        _biometricUnlockState.asStateFlow()
 
     private val _fingerprintSensorLocation = MutableStateFlow<Point?>(null)
     override val fingerprintSensorLocation: Flow<Point?> = _fingerprintSensorLocation
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt
index a6b40df..fb12897 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/KeyguardBlueprintRepositoryKosmos.kt
@@ -23,12 +23,14 @@
 import com.android.systemui.keyguard.ui.view.layout.blueprints.DefaultKeyguardBlueprint
 import com.android.systemui.keyguard.ui.view.layout.blueprints.SplitShadeKeyguardBlueprint
 import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection
+import com.android.systemui.keyguard.ui.view.layout.sections.SmartspaceSection
 import com.android.systemui.keyguard.ui.viewmodel.keyguardClockViewModel
 import com.android.systemui.keyguard.ui.viewmodel.keyguardRootViewModel
 import com.android.systemui.keyguard.ui.viewmodel.keyguardSmartspaceViewModel
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.util.mockito.mock
 import java.util.Optional
+import org.mockito.Mockito.spy
 
 val Kosmos.keyguardClockSection: ClockSection by
     Kosmos.Fixture {
@@ -42,6 +44,9 @@
         )
     }
 
+val Kosmos.keyguardSmartspaceSection: SmartspaceSection by
+    Kosmos.Fixture { mock<SmartspaceSection>() }
+
 val Kosmos.defaultKeyguardBlueprint by
     Kosmos.Fixture {
         DefaultKeyguardBlueprint(
@@ -57,7 +62,7 @@
             aodBurnInSection = mock(),
             communalTutorialIndicatorSection = mock(),
             clockSection = keyguardClockSection,
-            smartspaceSection = mock(),
+            smartspaceSection = keyguardSmartspaceSection,
             keyguardSliceViewSection = mock(),
             udfpsAccessibilityOverlaySection = mock(),
             accessibilityActionsSection = mock(),
@@ -80,7 +85,7 @@
             aodBurnInSection = mock(),
             communalTutorialIndicatorSection = mock(),
             clockSection = keyguardClockSection,
-            smartspaceSection = mock(),
+            smartspaceSection = keyguardSmartspaceSection,
             mediaSection = mock(),
             accessibilityActionsSection = mock(),
         )
@@ -88,13 +93,15 @@
 
 val Kosmos.keyguardBlueprintRepository by
     Kosmos.Fixture {
-        KeyguardBlueprintRepository(
-            blueprints =
-                setOf(
-                    defaultKeyguardBlueprint,
-                    splitShadeBlueprint,
-                ),
-            handler = fakeExecutorHandler,
-            assert = mock(),
+        spy(
+            KeyguardBlueprintRepository(
+                blueprints =
+                    setOf(
+                        defaultKeyguardBlueprint,
+                        splitShadeBlueprint,
+                    ),
+                handler = fakeExecutorHandler,
+                assert = mock(),
+            )
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/BiometricUnlockInteractorKosmos.kt
similarity index 67%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/BiometricUnlockInteractorKosmos.kt
index 277dbb7..7a3f925 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/BiometricUnlockInteractorKosmos.kt
@@ -14,8 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.keyguard.domain.interactor
 
+import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
 
-val Kosmos.iconLabelVisibilityRepository by Kosmos.Fixture { IconLabelVisibilityRepository() }
+val Kosmos.biometricUnlockInteractor by Fixture {
+    BiometricUnlockInteractor(
+        keyguardRepository = keyguardRepository,
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorKosmos.kt
index 5256ce4..4328ca1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorKosmos.kt
@@ -20,6 +20,8 @@
 import com.android.systemui.biometrics.domain.interactor.fingerprintPropertyInteractor
 import com.android.systemui.common.ui.domain.interactor.configurationInteractor
 import com.android.systemui.keyguard.data.repository.keyguardBlueprintRepository
+import com.android.systemui.keyguard.data.repository.keyguardClockSection
+import com.android.systemui.keyguard.data.repository.keyguardSmartspaceSection
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.shade.domain.interactor.shadeInteractor
@@ -34,5 +36,7 @@
             clockInteractor = keyguardClockInteractor,
             configurationInteractor = configurationInteractor,
             fingerprintPropertyInteractor = fingerprintPropertyInteractor,
+            clockSection = keyguardClockSection,
+            smartspaceSection = keyguardSmartspaceSection,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinderKosmos.kt
deleted file mode 100644
index 24d2c2f..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/KeyguardBlueprintViewBinderKosmos.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.ui.binder
-
-import android.os.fakeExecutorHandler
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.keyguardBlueprintViewBinder by
-    Kosmos.Fixture { KeyguardBlueprintViewBinder(fakeExecutorHandler) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelKosmos.kt
index 63b87c0..0c538ff 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBlueprintViewModelKosmos.kt
@@ -16,8 +16,14 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.os.fakeExecutorHandler
 import com.android.systemui.keyguard.domain.interactor.keyguardBlueprintInteractor
 import com.android.systemui.kosmos.Kosmos
 
 val Kosmos.keyguardBlueprintViewModel by
-    Kosmos.Fixture { KeyguardBlueprintViewModel(keyguardBlueprintInteractor) }
+    Kosmos.Fixture {
+        KeyguardBlueprintViewModel(
+            fakeExecutorHandler,
+            keyguardBlueprintInteractor,
+        )
+    }
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 f856d27..2567ffe 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
@@ -32,7 +32,7 @@
 
 val Kosmos.keyguardRootViewModel by Fixture {
     KeyguardRootViewModel(
-        scope = applicationCoroutineScope,
+        applicationScope = applicationCoroutineScope,
         deviceEntryInteractor = deviceEntryInteractor,
         dozeParameters = dozeParameters,
         keyguardInteractor = keyguardInteractor,
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 f2f4332..6d2d04a 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
@@ -27,8 +27,9 @@
 import com.android.systemui.classifier.falsingCollector
 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
 import com.android.systemui.common.ui.domain.interactor.configurationInteractor
-import com.android.systemui.communal.data.repository.fakeCommunalRepository
+import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
 import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.ui.viewmodel.communalTransitionViewModel
 import com.android.systemui.concurrency.fakeExecutor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
 import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
@@ -49,6 +50,7 @@
 import com.android.systemui.power.data.repository.fakePowerRepository
 import com.android.systemui.power.domain.interactor.powerInteractor
 import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.domain.startable.scrimStartable
 import com.android.systemui.scene.sceneContainerConfig
 import com.android.systemui.scene.shared.model.sceneDataSource
 import com.android.systemui.settings.brightness.domain.interactor.brightnessMirrorShowingInteractor
@@ -58,6 +60,7 @@
 import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel
 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.scrimController
 import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
 import com.android.systemui.statusbar.policy.data.repository.fakeDeviceProvisioningRepository
 import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor
@@ -86,7 +89,8 @@
     val configurationRepository by lazy { kosmos.fakeConfigurationRepository }
     val configurationInteractor by lazy { kosmos.configurationInteractor }
     val bouncerRepository by lazy { kosmos.bouncerRepository }
-    val communalRepository by lazy { kosmos.fakeCommunalRepository }
+    val communalRepository by lazy { kosmos.fakeCommunalSceneRepository }
+    val communalTransitionViewModel by lazy { kosmos.communalTransitionViewModel }
     val headsUpNotificationInteractor by lazy { kosmos.headsUpNotificationInteractor }
     val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
     val keyguardBouncerRepository by lazy { kosmos.fakeKeyguardBouncerRepository }
@@ -128,4 +132,6 @@
     val shadeInteractor by lazy { kosmos.shadeInteractor }
 
     val ongoingActivityChipsViewModel by lazy { kosmos.ongoingActivityChipsViewModel }
+    val scrimController by lazy { kosmos.scrimController }
+    val scrimStartable by lazy { kosmos.scrimStartable }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt
new file mode 100644
index 0000000..c4365c9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.mediaprojection.data.repository
+
+import android.app.ActivityManager
+import com.android.systemui.mediaprojection.data.model.MediaProjectionState
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeMediaProjectionRepository : MediaProjectionRepository {
+    override suspend fun switchProjectedTask(task: ActivityManager.RunningTaskInfo) {}
+
+    override val mediaProjectionState: MutableStateFlow<MediaProjectionState> =
+        MutableStateFlow(MediaProjectionState.NotProjecting)
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepositoryKosmos.kt
new file mode 100644
index 0000000..f253e94
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepositoryKosmos.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.mediaprojection.data.repository
+
+import android.os.Handler
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.mediaprojection.taskswitcher.activityTaskManagerTasksRepository
+import com.android.systemui.mediaprojection.taskswitcher.fakeMediaProjectionManager
+
+val Kosmos.fakeMediaProjectionRepository: FakeMediaProjectionRepository by
+    Kosmos.Fixture { FakeMediaProjectionRepository() }
+
+val Kosmos.realMediaProjectionRepository by
+    Kosmos.Fixture {
+        MediaProjectionManagerRepository(
+            mediaProjectionManager = fakeMediaProjectionManager.mediaProjectionManager,
+            handler = Handler.getMain(),
+            applicationScope = applicationCoroutineScope,
+            tasksRepository = activityTaskManagerTasksRepository,
+            backgroundDispatcher = testDispatcher,
+            mediaProjectionServiceHelper = fakeMediaProjectionManager.helper,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/TaskSwitcherKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/TaskSwitcherKosmos.kt
index d344b75..5acadd7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/TaskSwitcherKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/taskswitcher/TaskSwitcherKosmos.kt
@@ -16,12 +16,11 @@
 
 package com.android.systemui.mediaprojection.taskswitcher
 
-import android.os.Handler
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.mediaprojection.data.repository.realMediaProjectionRepository
 import com.android.systemui.mediaprojection.taskswitcher.data.repository.ActivityTaskManagerTasksRepository
-import com.android.systemui.mediaprojection.taskswitcher.data.repository.MediaProjectionManagerRepository
 import com.android.systemui.mediaprojection.taskswitcher.domain.interactor.TaskSwitchInteractor
 import com.android.systemui.mediaprojection.taskswitcher.ui.viewmodel.TaskSwitcherNotificationViewModel
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -40,21 +39,9 @@
         )
     }
 
-val Kosmos.mediaProjectionManagerRepository by
-    Kosmos.Fixture {
-        MediaProjectionManagerRepository(
-            mediaProjectionManager = fakeMediaProjectionManager.mediaProjectionManager,
-            handler = Handler.getMain(),
-            applicationScope = applicationCoroutineScope,
-            tasksRepository = activityTaskManagerTasksRepository,
-            backgroundDispatcher = testDispatcher,
-            mediaProjectionServiceHelper = fakeMediaProjectionManager.helper,
-        )
-    }
-
 val Kosmos.taskSwitcherInteractor by
     Kosmos.Fixture {
-        TaskSwitchInteractor(mediaProjectionManagerRepository, activityTaskManagerTasksRepository)
+        TaskSwitchInteractor(realMediaProjectionRepository, activityTaskManagerTasksRepository)
     }
 
 val Kosmos.taskSwitcherViewModel by
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryKosmos.kt
similarity index 70%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryKosmos.kt
index 277dbb7..39ae5eb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/QSPreferencesRepositoryKosmos.kt
@@ -17,5 +17,9 @@
 package com.android.systemui.qs.panels.data.repository
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.settings.userFileManager
+import com.android.systemui.user.data.repository.userRepository
 
-val Kosmos.iconLabelVisibilityRepository by Kosmos.Fixture { IconLabelVisibilityRepository() }
+val Kosmos.qsPreferencesRepository by
+    Kosmos.Fixture { QSPreferencesRepository(userFileManager, userRepository, testDispatcher) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorKosmos.kt
index 7b9e4a1..954084b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/IconLabelVisibilityInteractorKosmos.kt
@@ -19,12 +19,11 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.log.core.FakeLogBuffer
-import com.android.systemui.qs.panels.data.repository.iconLabelVisibilityRepository
 
 val Kosmos.iconLabelVisibilityInteractor by
     Kosmos.Fixture {
         IconLabelVisibilityInteractor(
-            iconLabelVisibilityRepository,
+            qsPreferencesInteractor,
             FakeLogBuffer.Factory.create(),
             applicationCoroutineScope
         )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/QSPreferencesInteractorKosmos.kt
similarity index 72%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/QSPreferencesInteractorKosmos.kt
index 277dbb7..eb83e32 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/QSPreferencesInteractorKosmos.kt
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.qs.panels.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.panels.data.repository.qsPreferencesRepository
 
-val Kosmos.iconLabelVisibilityRepository by Kosmos.Fixture { IconLabelVisibilityRepository() }
+val Kosmos.qsPreferencesInteractor by
+    Kosmos.Fixture { QSPreferencesInteractor(qsPreferencesRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt
index 604c16f..5ff44e5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/pipeline/data/repository/QSPipelineRepositoryKosmos.kt
@@ -17,6 +17,8 @@
 package com.android.systemui.qs.pipeline.data.repository
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.retail.data.repository.FakeRetailModeRepository
+import com.android.systemui.retail.data.repository.RetailModeRepository
 
 /** This fake uses 0 as the minimum number of tiles. That means that no tiles is a valid state. */
 var Kosmos.fakeMinimumTilesRepository by Kosmos.Fixture { MinimumTilesFixedRepository(0) }
@@ -46,3 +48,6 @@
 val Kosmos.fakeCustomTileAddedRepository by Kosmos.Fixture { FakeCustomTileAddedRepository() }
 var Kosmos.customTileAddedRepository: CustomTileAddedRepository by
     Kosmos.Fixture { fakeCustomTileAddedRepository }
+
+val Kosmos.fakeRetailModeRepository by Kosmos.Fixture { FakeRetailModeRepository() }
+var Kosmos.retailModeRepository: RetailModeRepository by Kosmos.Fixture { fakeRetailModeRepository }
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 b870039..d97a5b2 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
@@ -24,6 +24,7 @@
 import com.android.systemui.qs.pipeline.data.repository.customTileAddedRepository
 import com.android.systemui.qs.pipeline.data.repository.installedTilesRepository
 import com.android.systemui.qs.pipeline.data.repository.minimumTilesRepository
+import com.android.systemui.qs.pipeline.data.repository.retailModeRepository
 import com.android.systemui.qs.pipeline.data.repository.tileSpecRepository
 import com.android.systemui.qs.pipeline.shared.logging.qsLogger
 import com.android.systemui.qs.pipeline.shared.pipelineFlagsRepository
@@ -39,6 +40,7 @@
             installedTilesRepository,
             userRepository,
             minimumTilesRepository,
+            retailModeRepository,
             customTileStatePersister,
             { newQSTileFactory },
             qsTileFactory,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
index e0f60e9..b6194e3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/ui/adapter/FakeQSSceneAdapter.kt
@@ -19,8 +19,8 @@
 import android.content.Context
 import android.view.View
 import com.android.systemui.settings.brightness.MirrorController
-import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.flow.filterNotNull
 
@@ -41,7 +41,7 @@
     override val customizerAnimationDuration = _animationDuration.asStateFlow()
 
     private val _view = MutableStateFlow<View?>(null)
-    override val qsView: Flow<View> = _view.filterNotNull()
+    override val qsView: StateFlow<View?> = _view.asStateFlow()
 
     private val _state = MutableStateFlow<QSSceneAdapter.State?>(null)
     val state = _state.filterNotNull()
@@ -64,6 +64,8 @@
         }
     }
 
+    override fun applyLatestExpansionAndSquishiness() {}
+
     fun setCustomizing(value: Boolean) {
         updateCustomizerFlows(if (value) CustomizerState.Showing else CustomizerState.Hidden)
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/ScrimStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/ScrimStartableKosmos.kt
new file mode 100644
index 0000000..b64c840
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/ScrimStartableKosmos.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.scene.domain.startable
+
+import com.android.systemui.bouncer.domain.interactor.alternateBouncerInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
+import com.android.systemui.keyguard.domain.interactor.biometricUnlockInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInteractor
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.settings.brightness.domain.interactor.brightnessMirrorShowingInteractor
+import com.android.systemui.statusbar.phone.dozeServiceHost
+import com.android.systemui.statusbar.phone.scrimController
+import com.android.systemui.statusbar.phone.statusBarKeyguardViewManager
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+val Kosmos.scrimStartable by Fixture {
+    ScrimStartable(
+        applicationScope = applicationCoroutineScope,
+        scrimController = scrimController,
+        sceneInteractor = sceneInteractor,
+        deviceEntryInteractor = deviceEntryInteractor,
+        keyguardInteractor = keyguardInteractor,
+        occlusionInteractor = sceneContainerOcclusionInteractor,
+        biometricUnlockInteractor = biometricUnlockInteractor,
+        statusBarKeyguardViewManager = statusBarKeyguardViewManager,
+        alternateBouncerInteractor = alternateBouncerInteractor,
+        brightnessMirrorShowingInteractor = brightnessMirrorShowingInteractor,
+        dozeServiceHost = dozeServiceHost,
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt
index 297d1d8..0a3a2ee 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeLockscreenInteractorKosmos.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.shade.domain.interactor
 
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.shade.data.repository.shadeRepository
 import com.android.systemui.util.mockito.mock
@@ -25,7 +25,8 @@
 val Kosmos.shadeLockscreenInteractor by
     Kosmos.Fixture {
         ShadeLockscreenInteractorImpl(
-            scope = testScope,
+            applicationScope = applicationCoroutineScope,
+            backgroundScope = applicationCoroutineScope,
             shadeInteractor = shadeInteractorImpl,
             sceneInteractor = sceneInteractor,
             lockIconViewController = mock(),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt
index 8c5ff1d..c5625e4 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/QuickSettingsShadeSceneViewModelKosmos.kt
@@ -16,8 +16,12 @@
 
 package com.android.systemui.shade.ui.viewmodel
 
+import com.android.systemui.brightness.ui.viewmodel.brightnessSliderViewModel
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.qs.panels.ui.viewmodel.editModeViewModel
+import com.android.systemui.qs.panels.ui.viewmodel.tileGridViewModel
+import com.android.systemui.qs.ui.adapter.qsSceneAdapter
 import com.android.systemui.qs.ui.viewmodel.QuickSettingsShadeSceneViewModel
 
 val Kosmos.quickSettingsShadeSceneViewModel: QuickSettingsShadeSceneViewModel by
@@ -25,5 +29,9 @@
         QuickSettingsShadeSceneViewModel(
             applicationScope = applicationCoroutineScope,
             overlayShadeViewModel = overlayShadeViewModel,
+            brightnessSliderViewModel = brightnessSliderViewModel,
+            tileGridViewModel = tileGridViewModel,
+            editModeViewModel = editModeViewModel,
+            qsSceneAdapter = qsSceneAdapter,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
index 9e02df9..88bde2e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
@@ -19,7 +19,9 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository
 import com.android.systemui.screenrecord.data.repository.screenRecordRepository
+import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor
 import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor
 import com.android.systemui.util.time.fakeSystemClock
 
@@ -32,6 +34,15 @@
         )
     }
 
+val Kosmos.mediaProjectionChipInteractor: MediaProjectionChipInteractor by
+    Kosmos.Fixture {
+        MediaProjectionChipInteractor(
+            scope = applicationCoroutineScope,
+            mediaProjectionRepository = fakeMediaProjectionRepository,
+            systemClock = fakeSystemClock,
+        )
+    }
+
 val Kosmos.callChipInteractor: FakeCallChipInteractor by Kosmos.Fixture { FakeCallChipInteractor() }
 
 val Kosmos.ongoingActivityChipsViewModel: OngoingActivityChipsViewModel by
@@ -39,6 +50,7 @@
         OngoingActivityChipsViewModel(
             testScope.backgroundScope,
             screenRecordChipInteractor = screenRecordChipInteractor,
+            mediaProjectionChipInteractor = mediaProjectionChipInteractor,
             callChipInteractor = callChipInteractor,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerKosmos.kt
similarity index 73%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerKosmos.kt
index 277dbb7..569429f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerKosmos.kt
@@ -14,8 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+package com.android.systemui.statusbar.notification.stack
 
 import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.util.mockito.mock
 
-val Kosmos.iconLabelVisibilityRepository by Kosmos.Fixture { IconLabelVisibilityRepository() }
+val Kosmos.notificationStackScrollLayoutController by
+    Kosmos.Fixture { mock<NotificationStackScrollLayoutController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java
index beabaf5..3a70cdf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java
@@ -44,7 +44,7 @@
     }
 
     @Override
-    public void registerContentObserver(Uri uri, boolean notifyDescendants,
+    public void registerContentObserverSync(Uri uri, boolean notifyDescendants,
             ContentObserver settingsObserver) {
         List<ContentObserver> observers;
         mContentObserversAllUsers.putIfAbsent(uri.toString(), new ArrayList<>());
@@ -53,7 +53,7 @@
     }
 
     @Override
-    public void unregisterContentObserver(ContentObserver settingsObserver) {
+    public void unregisterContentObserverSync(ContentObserver settingsObserver) {
         for (Map.Entry<String, List<ContentObserver>> entry :
                 mContentObserversAllUsers.entrySet()) {
             entry.getValue().remove(settingsObserver);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
index a491886..cd219ec 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
@@ -66,7 +66,7 @@
     }
 
     @Override
-    public void registerContentObserverForUser(Uri uri, boolean notifyDescendants,
+    public void registerContentObserverForUserSync(Uri uri, boolean notifyDescendants,
             ContentObserver settingsObserver, int userHandle) {
         List<ContentObserver> observers;
         if (userHandle == UserHandle.USER_ALL) {
@@ -81,7 +81,7 @@
     }
 
     @Override
-    public void unregisterContentObserver(ContentObserver settingsObserver) {
+    public void unregisterContentObserverSync(ContentObserver settingsObserver) {
         for (SettingsKey key : mContentObservers.keySet()) {
             List<ContentObserver> observers = mContentObservers.get(key);
             observers.remove(settingsObserver);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/TestAudioDevicesFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/TestAudioDevicesFactory.kt
new file mode 100644
index 0000000..15ef26d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/TestAudioDevicesFactory.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.data.repository
+
+import android.annotation.SuppressLint
+import android.media.AudioDeviceInfo
+import android.media.AudioDevicePort
+
+@SuppressLint("VisibleForTests")
+object TestAudioDevicesFactory {
+
+    fun builtInDevice(deviceName: String = "built_in"): AudioDeviceInfo {
+        return AudioDeviceInfo(
+            AudioDevicePort.createForTesting(
+                AudioDeviceInfo.TYPE_BUILTIN_SPEAKER,
+                deviceName,
+                "",
+            )
+        )
+    }
+
+    fun wiredDevice(
+        deviceName: String = "wired",
+        deviceAddress: String = "card=1;device=0",
+    ): AudioDeviceInfo {
+        return AudioDeviceInfo(
+            AudioDevicePort.createForTesting(
+                AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
+                deviceName,
+                deviceAddress,
+            )
+        )
+    }
+
+    fun bluetoothDevice(
+        deviceName: String = "bt",
+        deviceAddress: String = "00:43:A8:23:10:F0",
+    ): AudioDeviceInfo {
+        return AudioDeviceInfo(
+            AudioDevicePort.createForTesting(
+                AudioDeviceInfo.TYPE_BLE_HEADSET,
+                deviceName,
+                deviceAddress,
+            )
+        )
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt
index 63c3ee5..e2d414e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt
@@ -16,18 +16,19 @@
 
 package com.android.systemui.volume.domain.interactor
 
+import android.content.applicationContext
 import com.android.systemui.bluetooth.bluetoothAdapter
 import com.android.systemui.bluetooth.localBluetoothManager
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.volume.data.repository.audioRepository
 import com.android.systemui.volume.data.repository.audioSharingRepository
-import com.android.systemui.volume.localMediaRepositoryFactory
 import com.android.systemui.volume.mediaOutputInteractor
 
 val Kosmos.audioOutputInteractor by
     Kosmos.Fixture {
         AudioOutputInteractor(
+            applicationContext,
             audioRepository,
             audioModeInteractor,
             testScope.backgroundScope,
@@ -36,7 +37,6 @@
             bluetoothAdapter,
             deviceIconInteractor,
             mediaOutputInteractor,
-            localMediaRepositoryFactory,
             audioSharingRepository,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/CaptioningModuleKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/CaptioningModuleKosmos.kt
new file mode 100644
index 0000000..e7162d2
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/CaptioningModuleKosmos.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.volume.panel.component.captioning
+
+import com.android.internal.logging.uiEventLogger
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.view.accessibility.data.repository.captioningInteractor
+import com.android.systemui.volume.panel.component.button.ui.composable.ToggleButtonComponent
+import com.android.systemui.volume.panel.component.captioning.domain.CaptioningAvailabilityCriteria
+import com.android.systemui.volume.panel.component.captioning.ui.viewmodel.captioningViewModel
+
+val Kosmos.captioningComponent by
+    Kosmos.Fixture {
+        ToggleButtonComponent(
+            captioningViewModel.buttonViewModel,
+            captioningViewModel::setIsSystemAudioCaptioningEnabled,
+        )
+    }
+val Kosmos.captioningAvailabilityCriteria by
+    Kosmos.Fixture {
+        CaptioningAvailabilityCriteria(
+            captioningInteractor,
+            testScope.backgroundScope,
+            uiEventLogger,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelKosmos.kt
new file mode 100644
index 0000000..0edd9e0
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelKosmos.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.captioning.ui.viewmodel
+
+import android.content.applicationContext
+import com.android.internal.logging.uiEventLogger
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.view.accessibility.data.repository.captioningInteractor
+
+val Kosmos.captioningViewModel by
+    Kosmos.Fixture {
+        CaptioningViewModel(
+            applicationContext,
+            captioningInteractor,
+            testScope.backgroundScope,
+            uiEventLogger,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/FakeLocalMediaRepositoryFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/FakeLocalMediaRepositoryFactory.kt
index 9c902cf..680535d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/FakeLocalMediaRepositoryFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/data/repository/FakeLocalMediaRepositoryFactory.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.volume.panel.component.mediaoutput.data.repository
 
 import com.android.settingslib.volume.data.repository.LocalMediaRepository
+import kotlinx.coroutines.CoroutineScope
 
 class FakeLocalMediaRepositoryFactory(private val defaultProvider: () -> LocalMediaRepository) :
     LocalMediaRepositoryFactory {
@@ -27,6 +28,8 @@
         repositories[packageName] = localMediaRepository
     }
 
-    override fun create(packageName: String?): LocalMediaRepository =
-        repositories[packageName] ?: defaultProvider()
+    override fun create(
+        packageName: String?,
+        coroutineScope: CoroutineScope
+    ): LocalMediaRepository = repositories[packageName] ?: defaultProvider()
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorKosmos.kt
new file mode 100644
index 0000000..9f11822
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.volume.domain.interactor.audioModeInteractor
+import com.android.systemui.volume.domain.interactor.audioOutputInteractor
+import com.android.systemui.volume.mediaDeviceSessionInteractor
+import com.android.systemui.volume.mediaOutputInteractor
+
+val Kosmos.mediaOutputComponentInteractor by
+    Kosmos.Fixture {
+        MediaOutputComponentInteractor(
+            testScope.backgroundScope,
+            mediaDeviceSessionInteractor,
+            audioOutputInteractor,
+            audioModeInteractor,
+            mediaOutputInteractor,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt
index 4029609..141f242 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/TestMediaDevicesFactory.kt
@@ -18,6 +18,7 @@
 
 import android.annotation.SuppressLint
 import android.bluetooth.BluetoothDevice
+import android.graphics.drawable.Drawable
 import android.graphics.drawable.TestStubDrawable
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
 import com.android.settingslib.media.BluetoothMediaDevice
@@ -29,29 +30,44 @@
 @SuppressLint("StaticFieldLeak") // These are mocks
 object TestMediaDevicesFactory {
 
-    fun builtInMediaDevice(): MediaDevice = mock {
-        whenever(name).thenReturn("built_in_media")
-        whenever(icon).thenReturn(TestStubDrawable())
+    fun builtInMediaDevice(
+        deviceName: String = "built_in_media",
+        deviceIcon: Drawable? = TestStubDrawable(),
+    ): MediaDevice = mock {
+        whenever(name).thenReturn(deviceName)
+        whenever(icon).thenReturn(deviceIcon)
     }
 
-    fun wiredMediaDevice(): MediaDevice =
+    fun wiredMediaDevice(
+        deviceName: String = "wired_media",
+        deviceIcon: Drawable? = TestStubDrawable(),
+    ): MediaDevice =
         mock<PhoneMediaDevice> {
             whenever(deviceType)
                 .thenReturn(MediaDevice.MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE)
-            whenever(name).thenReturn("wired_media")
-            whenever(icon).thenReturn(TestStubDrawable())
+            whenever(name).thenReturn(deviceName)
+            whenever(icon).thenReturn(deviceIcon)
         }
 
-    fun bluetoothMediaDevice(): MediaDevice {
-        val bluetoothDevice = mock<BluetoothDevice>()
+    fun bluetoothMediaDevice(
+        deviceName: String = "bt_media",
+        deviceIcon: Drawable? = TestStubDrawable(),
+        deviceAddress: String = "bt_media_device",
+    ): BluetoothMediaDevice {
+        val bluetoothDevice =
+            mock<BluetoothDevice> {
+                whenever(name).thenReturn(deviceName)
+                whenever(address).thenReturn(deviceAddress)
+            }
         val cachedBluetoothDevice: CachedBluetoothDevice = mock {
             whenever(isHearingAidDevice).thenReturn(true)
-            whenever(address).thenReturn("bt_media_device")
+            whenever(address).thenReturn(deviceAddress)
             whenever(device).thenReturn(bluetoothDevice)
+            whenever(name).thenReturn(deviceName)
         }
         return mock<BluetoothMediaDevice> {
-            whenever(name).thenReturn("bt_media")
-            whenever(icon).thenReturn(TestStubDrawable())
+            whenever(name).thenReturn(deviceName)
+            whenever(icon).thenReturn(deviceIcon)
             whenever(cachedDevice).thenReturn(cachedBluetoothDevice)
         }
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelKosmos.kt
index 6d4576e..2cd6ff2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelKosmos.kt
@@ -20,11 +20,8 @@
 import com.android.internal.logging.uiEventLogger
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.volume.domain.interactor.audioModeInteractor
-import com.android.systemui.volume.domain.interactor.audioOutputInteractor
-import com.android.systemui.volume.mediaDeviceSessionInteractor
 import com.android.systemui.volume.mediaOutputActionsInteractor
-import com.android.systemui.volume.mediaOutputInteractor
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.mediaOutputComponentInteractor
 
 var Kosmos.mediaOutputViewModel by
     Kosmos.Fixture {
@@ -32,10 +29,7 @@
             applicationContext,
             testScope.backgroundScope,
             mediaOutputActionsInteractor,
-            mediaDeviceSessionInteractor,
-            audioOutputInteractor,
-            audioModeInteractor,
-            mediaOutputInteractor,
+            mediaOutputComponentInteractor,
             uiEventLogger,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/SpatialAudioModuleKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/SpatialAudioModuleKosmos.kt
new file mode 100644
index 0000000..ea5d70d
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/SpatialAudioModuleKosmos.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.spatial
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.volume.panel.component.button.ui.composable.ButtonComponent
+import com.android.systemui.volume.panel.component.spatial.domain.SpatialAudioAvailabilityCriteria
+import com.android.systemui.volume.panel.component.spatial.domain.interactor.spatialAudioComponentInteractor
+import com.android.systemui.volume.panel.component.spatial.ui.viewmodel.spatialAudioViewModel
+
+val Kosmos.spatialAudioComponent by
+    Kosmos.Fixture { ButtonComponent(spatialAudioViewModel.spatialAudioButton) { _, _ -> } }
+val Kosmos.spatialAudioAvailabilityCriteria by
+    Kosmos.Fixture { SpatialAudioAvailabilityCriteria(spatialAudioComponentInteractor) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorKosmos.kt
new file mode 100644
index 0000000..95a7b9b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/domain/interactor/SpatialAudioComponentInteractorKosmos.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.spatial.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.media.spatializerInteractor
+import com.android.systemui.volume.domain.interactor.audioOutputInteractor
+
+val Kosmos.spatialAudioComponentInteractor by
+    Kosmos.Fixture {
+        SpatialAudioComponentInteractor(
+            audioOutputInteractor,
+            spatializerInteractor,
+            testScope.backgroundScope,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModelKosmos.kt
new file mode 100644
index 0000000..1b8a3fc
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModelKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.spatial.ui.viewmodel
+
+import android.content.applicationContext
+import com.android.internal.logging.uiEventLogger
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.volume.panel.component.spatial.domain.interactor.spatialAudioComponentInteractor
+import com.android.systemui.volume.panel.component.spatial.spatialAudioAvailabilityCriteria
+
+val Kosmos.spatialAudioViewModel by
+    Kosmos.Fixture {
+        SpatialAudioViewModel(
+            applicationContext,
+            testScope.backgroundScope,
+            spatialAudioAvailabilityCriteria,
+            spatialAudioComponentInteractor,
+            uiEventLogger,
+        )
+    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorKosmos.kt
index 8862942..a18f498 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/domain/interactor/ComponentsInteractorKosmos.kt
@@ -19,8 +19,10 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.volume.panel.component.bottombar.ui.bottomBarAvailabilityCriteria
+import com.android.systemui.volume.panel.component.captioning.captioningAvailabilityCriteria
 import com.android.systemui.volume.panel.component.mediaoutput.mediaOutputAvailabilityCriteria
 import com.android.systemui.volume.panel.component.shared.model.VolumePanelComponents
+import com.android.systemui.volume.panel.component.spatial.spatialAudioAvailabilityCriteria
 import com.android.systemui.volume.panel.component.volume.volumeSlidersAvailabilityCriteria
 import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
 import com.android.systemui.volume.panel.domain.defaultCriteria
@@ -36,6 +38,8 @@
         mapOf(
             VolumePanelComponents.MEDIA_OUTPUT to Provider { mediaOutputAvailabilityCriteria },
             VolumePanelComponents.VOLUME_SLIDERS to Provider { volumeSlidersAvailabilityCriteria },
+            VolumePanelComponents.CAPTIONING to Provider { captioningAvailabilityCriteria },
+            VolumePanelComponents.SPATIAL_AUDIO to Provider { spatialAudioAvailabilityCriteria },
             VolumePanelComponents.BOTTOM_BAR to Provider { bottomBarAvailabilityCriteria },
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/composable/ComponentsFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/composable/ComponentsFactoryKosmos.kt
index bacf22c..6bea416 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/composable/ComponentsFactoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/ui/composable/ComponentsFactoryKosmos.kt
@@ -18,8 +18,10 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.volume.panel.component.bottombar.ui.bottomBarComponent
+import com.android.systemui.volume.panel.component.captioning.captioningComponent
 import com.android.systemui.volume.panel.component.mediaoutput.mediaOutputComponent
 import com.android.systemui.volume.panel.component.shared.model.VolumePanelComponents
+import com.android.systemui.volume.panel.component.spatial.spatialAudioComponent
 import com.android.systemui.volume.panel.component.volume.volumeSlidersComponent
 import com.android.systemui.volume.panel.shared.model.VolumePanelComponentKey
 import com.android.systemui.volume.panel.shared.model.VolumePanelUiComponent
@@ -30,9 +32,11 @@
 var Kosmos.prodComponentByKey: Map<VolumePanelComponentKey, Provider<VolumePanelUiComponent>> by
     Kosmos.Fixture {
         mapOf(
-            VolumePanelComponents.BOTTOM_BAR to Provider { bottomBarComponent },
             VolumePanelComponents.MEDIA_OUTPUT to Provider { mediaOutputComponent },
             VolumePanelComponents.VOLUME_SLIDERS to Provider { volumeSlidersComponent },
+            VolumePanelComponents.CAPTIONING to Provider { captioningComponent },
+            VolumePanelComponents.SPATIAL_AUDIO to Provider { spatialAudioComponent },
+            VolumePanelComponents.BOTTOM_BAR to Provider { bottomBarComponent },
         )
     }
 var Kosmos.enabledComponents: Collection<VolumePanelComponentKey> by
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index 004f37c..2c4bc7c 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -2514,7 +2514,7 @@
         public Plane[] getPlanes() {
             throwISEIfImageIsInvalid();
             if (mPlanes == null) {
-                int fenceFd = mParcelImage.fence != null ? mParcelImage.fence.getFd() : -1;
+                int fenceFd = mParcelImage.fence != null ? mParcelImage.fence.detachFd() : -1;
                 mGraphicBuffer = GraphicBuffer.createFromHardwareBuffer(mParcelImage.buffer);
                 mPlanes = ImageReader.initializeImagePlanes(mParcelImage.planeCount, mGraphicBuffer,
                         fenceFd, mParcelImage.format, mParcelImage.timestamp,
diff --git a/packages/services/VirtualCamera/OWNERS b/packages/services/VirtualCamera/OWNERS
deleted file mode 100644
index c66443f..0000000
--- a/packages/services/VirtualCamera/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-include /services/companion/java/com/android/server/companion/virtual/OWNERS
-caen@google.com
-jsebechlebsky@google.com
\ No newline at end of file
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index 1c57dd3..9353150 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -220,3 +220,13 @@
     description: "Feature allows users to change color correction saturation for daltonizer."
     bug: "322829049"
 }
+
+flag {
+    name: "skip_package_change_before_user_switch"
+    namespace: "accessibility"
+    description: "Skip onSomePackageChanged callback if the SwitchUser signal is not received yet."
+    bug: "340927041"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 42f168b..7342b00 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1379,6 +1379,30 @@
         }
     }
 
+    @RequiresNoPermission
+    @Override
+    public boolean isMagnificationSystemUIConnected() {
+        if (svcConnTracingEnabled()) {
+            logTraceSvcConn("isMagnificationSystemUIConnected", "");
+        }
+        synchronized (mLock) {
+            if (!hasRightsToCurrentUserLocked()) {
+                return false;
+            }
+            if (!mSecurityPolicy.canControlMagnification(this)) {
+                return false;
+            }
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                MagnificationProcessor magnificationProcessor =
+                        mSystemSupport.getMagnificationProcessor();
+                return magnificationProcessor.isMagnificationSystemUIConnected();
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+
     public boolean isMagnificationCallbackEnabled(int displayId) {
         return mInvocationHandler.isMagnificationCallbackEnabled(displayId);
     }
@@ -1724,9 +1748,7 @@
     }
 
     public void resetLocked() {
-        if (Flags.resettableDynamicProperties()) {
-            mAccessibilityServiceInfo.resetDynamicallyConfigurableProperties();
-        }
+        mAccessibilityServiceInfo.resetDynamicallyConfigurableProperties();
         mSystemSupport.getKeyEventDispatcher().flush(this);
         try {
             // Clear the proxy in the other process so this
@@ -1925,6 +1947,11 @@
                 InvocationHandler.MSG_CLEAR_ACCESSIBILITY_CACHE);
     }
 
+    public void notifyMagnificationSystemUIConnectionChangedLocked(boolean connected) {
+        mInvocationHandler
+                .notifyMagnificationSystemUIConnectionChangedLocked(connected);
+    }
+
     public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
             @NonNull MagnificationConfig config) {
         mInvocationHandler
@@ -1976,6 +2003,21 @@
         return (mGenericMotionEventSources & eventSourceWithoutClass) != 0;
     }
 
+    /**
+     * Called by the invocation handler to notify the service that the
+     * magnification systemui connection has changed.
+     */
+    private void notifyMagnificationSystemUIConnectionChangedInternal(boolean connected) {
+        final IAccessibilityServiceClient listener = getServiceInterfaceSafely();
+        if (listener != null) {
+            try {
+                listener.onMagnificationSystemUIConnectionChanged(connected);
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG,
+                        "Error sending magnification sysui connection changes to " + mService, re);
+            }
+        }
+    }
 
     /**
      * Called by the invocation handler to notify the service that the
@@ -2372,6 +2414,7 @@
         private static final int MSG_BIND_INPUT = 12;
         private static final int MSG_UNBIND_INPUT = 13;
         private static final int MSG_START_INPUT = 14;
+        private static final int MSG_ON_MAGNIFICATION_SYSTEM_UI_CONNECTION_CHANGED = 15;
 
         /** List of magnification callback states, mapping from displayId -> Boolean */
         @GuardedBy("mlock")
@@ -2398,6 +2441,13 @@
                     notifyClearAccessibilityCacheInternal();
                 } break;
 
+                case MSG_ON_MAGNIFICATION_SYSTEM_UI_CONNECTION_CHANGED: {
+                    final SomeArgs args = (SomeArgs) message.obj;
+                    final boolean connected = args.argi1 == 1;
+                    notifyMagnificationSystemUIConnectionChangedInternal(connected);
+                    args.recycle();
+                } break;
+
                 case MSG_ON_MAGNIFICATION_CHANGED: {
                     final SomeArgs args = (SomeArgs) message.obj;
                     final Region region = (Region) args.arg1;
@@ -2455,6 +2505,15 @@
             }
         }
 
+        public void notifyMagnificationSystemUIConnectionChangedLocked(boolean connected) {
+            final SomeArgs args = SomeArgs.obtain();
+            args.argi1 = connected ? 1 : 0;
+
+            final Message msg =
+                    obtainMessage(MSG_ON_MAGNIFICATION_SYSTEM_UI_CONNECTION_CHANGED, args);
+            msg.sendToTarget();
+        }
+
         public void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
                 @NonNull MagnificationConfig config) {
             synchronized (mLock) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 053a1a7..d09cb3e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -588,6 +588,15 @@
         return Thread.holdsLock(mLock);
     }
 
+    /**
+     * Returns if the service is initialized.
+     *
+     * The service is considered initialized when the user switch happened.
+     */
+    private boolean isServiceInitializedLocked() {
+        return mInitialized;
+    }
+
     @Override
     public int getCurrentUserIdLocked() {
         return mCurrentUserId;
@@ -1803,6 +1812,17 @@
     }
 
     /**
+     * Called by the MagnificationController when the magnification systemui connection changes.
+     *
+     * @param connected Whether the connection is ready.
+     */
+    public void notifyMagnificationSystemUIConnectionChanged(boolean connected) {
+        synchronized (mLock) {
+            notifyMagnificationSystemUIConnectionChangedLocked(connected);
+        }
+    }
+
+    /**
      * Called by the MagnificationController when the state of display
      * magnification changes.
      *
@@ -2234,6 +2254,14 @@
         mProxyManager.clearCacheLocked();
     }
 
+    private void notifyMagnificationSystemUIConnectionChangedLocked(boolean connected) {
+        final AccessibilityUserState state = getCurrentUserStateLocked();
+        for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
+            final AccessibilityServiceConnection service = state.mBoundServices.get(i);
+            service.notifyMagnificationSystemUIConnectionChangedLocked(connected);
+        }
+    }
+
     private void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
             @NonNull MagnificationConfig config) {
         final AccessibilityUserState state = getCurrentUserStateLocked();
@@ -5244,13 +5272,9 @@
 
                 //Clip to the window bounds.
                 Rect windowBounds = mTempRect1;
-                if (Flags.focusClickPointWindowBoundsFromA11yWindowInfo()) {
-                    AccessibilityWindowInfo window = focus.getWindow();
-                    if (window != null) {
-                        window.getBoundsInScreen(windowBounds);
-                    }
-                } else {
-                    getWindowBounds(focus.getWindowId(), windowBounds);
+                AccessibilityWindowInfo window = focus.getWindow();
+                if (window != null) {
+                    window.getBoundsInScreen(windowBounds);
                 }
                 if (!boundsInScreenBeforeMagnification.intersect(windowBounds)) {
                     return false;
@@ -6234,6 +6258,15 @@
                 if (userId != mManagerService.getCurrentUserIdLocked()) {
                     return;
                 }
+
+                // Only continue setting up the packages if the service has been initialized.
+                // See: b/340927041
+                if (Flags.skipPackageChangeBeforeUserSwitch()
+                        && !mManagerService.isServiceInitializedLocked()) {
+                    Slog.w(LOG_TAG,
+                            "onSomePackagesChanged: service not initialized, skip the callback.");
+                    return;
+                }
                 mManagerService.onSomePackagesChangedLocked(parsedAccessibilityServiceInfos,
                         parsedAccessibilityShortcutInfos);
             }
diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
index 4cb3d24..420bac7 100644
--- a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java
@@ -489,6 +489,14 @@
     /** @throws UnsupportedOperationException since a proxy does not need magnification */
     @RequiresNoPermission
     @Override
+    public boolean isMagnificationSystemUIConnected() throws UnsupportedOperationException {
+        throw new UnsupportedOperationException("isMagnificationSystemUIConnected is not"
+                + " supported");
+    }
+
+    /** @throws UnsupportedOperationException since a proxy does not need magnification */
+    @RequiresNoPermission
+    @Override
     public boolean isMagnificationCallbackEnabled(int displayId) {
         throw new UnsupportedOperationException("isMagnificationCallbackEnabled is not supported");
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
index 0719eba..7f4c808 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationConnectionManager.java
@@ -126,6 +126,7 @@
 
     @ConnectionState
     private int mConnectionState = DISCONNECTED;
+    ConnectionStateChangedCallback mConnectionStateChangedCallback = null;
 
     private static final int WAIT_CONNECTION_TIMEOUT_MILLIS = 100;
 
@@ -264,6 +265,9 @@
                 }
             }
         }
+        if (mConnectionStateChangedCallback != null) {
+            mConnectionStateChangedCallback.onConnectionStateChanged(connection != null);
+        }
     }
 
     /**
@@ -271,7 +275,7 @@
      */
     public boolean isConnected() {
         synchronized (mLock) {
-            return mConnectionWrapper != null;
+            return mConnectionWrapper != null && mConnectionState == CONNECTED;
         }
     }
 
@@ -1344,4 +1348,8 @@
             }
         }
     }
+
+    interface ConnectionStateChangedCallback {
+        void onConnectionStateChanged(boolean connected);
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 76367a2..9b78847 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -828,6 +828,8 @@
                 mMagnificationConnectionManager = new MagnificationConnectionManager(mContext,
                         mLock, this, mAms.getTraceManager(),
                         mScaleProvider);
+                mMagnificationConnectionManager.mConnectionStateChangedCallback =
+                        mAms::notifyMagnificationSystemUIConnectionChanged;
             }
             return mMagnificationConnectionManager;
         }
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
index ed8f1ab..6036839 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationProcessor.java
@@ -147,6 +147,10 @@
         return false;
     }
 
+    public boolean isMagnificationSystemUIConnected() {
+        return mController.getMagnificationConnectionManager().isConnected();
+    }
+
     private boolean setScaleAndCenterForFullScreenMagnification(int displayId, float scale,
             float centerX, float centerY, boolean animate, int id) {
 
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index dc1cfb9..ddccb37 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -4008,20 +4008,10 @@
     }
 
     private PackageInfo getPackageInfoForBMMLogging(String packageName) {
-        try {
-            return mPackageManager.getPackageInfoAsUser(packageName, 0, mUserId);
-        } catch (NameNotFoundException e) {
-            Slog.w(
-                    TAG,
-                    addUserIdToLogMessage(
-                            mUserId, "Asked to get PackageInfo for BMM logging of nonexistent pkg "
-                                    + packageName));
+        PackageInfo packageInfo = new PackageInfo();
+        packageInfo.packageName = packageName;
 
-            PackageInfo packageInfo = new PackageInfo();
-            packageInfo.packageName = packageName;
-
-            return packageInfo;
-        }
+        return packageInfo;
     }
 
     /** Hand off a restore session. */
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 30e4a3e..0d0c21d 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -34,7 +34,6 @@
 import static com.android.server.companion.utils.PackageUtils.enforceUsesCompanionDeviceFeature;
 import static com.android.server.companion.utils.PackageUtils.getPackageInfo;
 import static com.android.server.companion.utils.PackageUtils.isRestrictedSettingsAllowed;
-import static com.android.server.companion.utils.PermissionsUtils.checkCallerCanManageCompanionDevice;
 import static com.android.server.companion.utils.PermissionsUtils.enforceCallerCanManageAssociationsForPackage;
 import static com.android.server.companion.utils.PermissionsUtils.enforceCallerIsSystemOr;
 import static com.android.server.companion.utils.PermissionsUtils.enforceCallerIsSystemOrCanInteractWithUserId;
@@ -335,12 +334,6 @@
             enforceCallerCanManageAssociationsForPackage(getContext(), userId, packageName,
                     "get associations");
 
-            if (!checkCallerCanManageCompanionDevice(getContext())) {
-                // If the caller neither is system nor holds MANAGE_COMPANION_DEVICES: it needs to
-                // request the feature (also: the caller is the app itself).
-                enforceUsesCompanionDeviceFeature(getContext(), userId, packageName);
-            }
-
             return mAssociationStore.getActiveAssociationsByPackage(userId, packageName);
         }
 
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 d09d7e6..3fbd856 100644
--- a/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
@@ -347,6 +347,8 @@
      * Set association tag.
      */
     public void setAssociationTag(int associationId, String tag) {
+        Slog.i(TAG, "Setting association tag=[" + tag + "] to id=[" + associationId + "]...");
+
         AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(
                 associationId);
         association = (new AssociationInfo.Builder(association)).setTag(tag).build();
diff --git a/services/companion/java/com/android/server/companion/association/AssociationStore.java b/services/companion/java/com/android/server/companion/association/AssociationStore.java
index 29e8095..757abd9 100644
--- a/services/companion/java/com/android/server/companion/association/AssociationStore.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationStore.java
@@ -18,7 +18,7 @@
 
 import static com.android.server.companion.utils.MetricUtils.logCreateAssociation;
 import static com.android.server.companion.utils.MetricUtils.logRemoveAssociation;
-import static com.android.server.companion.utils.PermissionsUtils.checkCallerCanManageAssociationsForPackage;
+import static com.android.server.companion.utils.PermissionsUtils.enforceCallerCanManageAssociationsForPackage;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -457,6 +457,10 @@
 
     /**
      * Get association by id with caller checks.
+     *
+     * If the association is not found, an IllegalArgumentException would be thrown.
+     *
+     * If the caller can't access the association, a SecurityException would be thrown.
      */
     @NonNull
     public AssociationInfo getAssociationWithCallerChecks(int associationId) {
@@ -466,13 +470,9 @@
                     "getAssociationWithCallerChecks() Association id=[" + associationId
                             + "] doesn't exist.");
         }
-        if (checkCallerCanManageAssociationsForPackage(mContext, association.getUserId(),
-                association.getPackageName())) {
-            return association;
-        }
-
-        throw new IllegalArgumentException(
-                "The caller can't interact with the association id=[" + associationId + "].");
+        enforceCallerCanManageAssociationsForPackage(mContext, association.getUserId(),
+                association.getPackageName(), null);
+        return association;
     }
 
     /**
diff --git a/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java b/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java
index 8c1116b..6f0baef 100644
--- a/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java
+++ b/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java
@@ -98,7 +98,6 @@
         Slog.i(TAG, "Disassociating id=[" + id + "]...");
 
         final AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(id);
-
         final int userId = association.getUserId();
         final String packageName = association.getPackageName();
         final String deviceProfile = association.getDeviceProfile();
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
index 026d29c..00e049c 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -122,7 +122,6 @@
      */
     public boolean isPermissionTransferUserConsented(int associationId) {
         mAssociationStore.getAssociationWithCallerChecks(associationId);
-
         PermissionSyncRequest request = getPermissionSyncRequest(associationId);
         if (request == null) {
             return false;
@@ -147,12 +146,12 @@
             return null;
         }
 
-        final AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(
-                associationId);
-
         Slog.i(LOG_TAG, "Creating permission sync intent for userId [" + userId
                 + "] associationId [" + associationId + "]");
 
+        final AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(
+                associationId);
+
         // Create an internal intent to launch the user consent dialog
         final Bundle extras = new Bundle();
         PermissionSyncRequest request = new PermissionSyncRequest(associationId);
@@ -220,7 +219,9 @@
      * Enable perm sync for the association
      */
     public void enablePermissionsSync(int associationId) {
-        int userId = mAssociationStore.getAssociationWithCallerChecks(associationId).getUserId();
+        AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(
+                associationId);
+        int userId = association.getUserId();
         PermissionSyncRequest request = new PermissionSyncRequest(associationId);
         request.setUserConsented(true);
         mSystemDataTransferRequestStore.writeRequest(userId, request);
@@ -230,7 +231,9 @@
      * Disable perm sync for the association
      */
     public void disablePermissionsSync(int associationId) {
-        int userId = mAssociationStore.getAssociationWithCallerChecks(associationId).getUserId();
+        AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(
+                associationId);
+        int userId = association.getUserId();
         PermissionSyncRequest request = new PermissionSyncRequest(associationId);
         request.setUserConsented(false);
         mSystemDataTransferRequestStore.writeRequest(userId, request);
@@ -241,8 +244,9 @@
      */
     @Nullable
     public PermissionSyncRequest getPermissionSyncRequest(int associationId) {
-        int userId = mAssociationStore.getAssociationWithCallerChecks(associationId)
-                .getUserId();
+        AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(
+                associationId);
+        int userId = association.getUserId();
         List<SystemDataTransferRequest> requests =
                 mSystemDataTransferRequestStore.readRequestsByAssociationId(userId,
                         associationId);
@@ -259,7 +263,9 @@
      */
     public void removePermissionSyncRequest(int associationId) {
         Binder.withCleanCallingIdentity(() -> {
-            int userId = mAssociationStore.getAssociationById(associationId).getUserId();
+            AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(
+                    associationId);
+            int userId = association.getUserId();
             mSystemDataTransferRequestStore.removeRequestsByAssociationId(userId, associationId);
         });
     }
diff --git a/services/companion/java/com/android/server/companion/devicepresence/BleDeviceProcessor.java b/services/companion/java/com/android/server/companion/devicepresence/BleDeviceProcessor.java
index 6cdc02e..0a1bc0f 100644
--- a/services/companion/java/com/android/server/companion/devicepresence/BleDeviceProcessor.java
+++ b/services/companion/java/com/android/server/companion/devicepresence/BleDeviceProcessor.java
@@ -241,7 +241,8 @@
         final BroadcastReceiver receiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
-                checkBleState();
+                // Post to the main thread to make sure it is a Non-Blocking call.
+                new Handler(Looper.getMainLooper()).post(() -> checkBleState());
             }
         };
 
diff --git a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
index f397814..796d285 100644
--- a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
@@ -149,21 +149,6 @@
     }
 
     /**
-     * Check if the caller is system UID or the provided user.
-     */
-    public static boolean checkCallerIsSystemOr(@UserIdInt int userId,
-            @NonNull String packageName) {
-        final int callingUid = getCallingUid();
-        if (callingUid == SYSTEM_UID) return true;
-
-        if (getCallingUserId() != userId) return false;
-
-        if (!checkPackage(callingUid, packageName)) return false;
-
-        return true;
-    }
-
-    /**
      * Check if the calling user id matches the userId, and if the package belongs to
      * the calling uid.
      */
@@ -184,21 +169,30 @@
     }
 
     /**
-     * Check if the caller holds the necessary permission to manage companion devices.
-     */
-    public static boolean checkCallerCanManageCompanionDevice(@NonNull Context context) {
-        if (getCallingUid() == SYSTEM_UID) return true;
-
-        return context.checkCallingPermission(MANAGE_COMPANION_DEVICES) == PERMISSION_GRANTED;
-    }
-
-    /**
      * Require the caller to be able to manage the associations for the package.
      */
     public static void enforceCallerCanManageAssociationsForPackage(@NonNull Context context,
             @UserIdInt int userId, @NonNull String packageName,
             @Nullable String actionDescription) {
-        if (checkCallerCanManageAssociationsForPackage(context, userId, packageName)) return;
+        final int callingUid = getCallingUid();
+
+        // If the caller is the system
+        if (callingUid == SYSTEM_UID) {
+            return;
+        }
+
+        // If caller can manage the package or has the permissions to manage companion devices
+        boolean canInteractAcrossUsers = context.checkCallingPermission(INTERACT_ACROSS_USERS)
+                == PERMISSION_GRANTED;
+        boolean canManageCompanionDevices = context.checkCallingPermission(MANAGE_COMPANION_DEVICES)
+                == PERMISSION_GRANTED;
+        if (getCallingUserId() == userId) {
+            if (checkPackage(callingUid, packageName) || canManageCompanionDevices) {
+                return;
+            }
+        } else if (canInteractAcrossUsers && canManageCompanionDevices) {
+            return;
+        }
 
         throw new SecurityException("Caller (uid=" + getCallingUid() + ") does not have "
                 + "permissions to "
@@ -219,25 +213,6 @@
         }
     }
 
-    /**
-     * Check if the caller is either:
-     * <ul>
-     * <li> the package itself
-     * <li> the System ({@link android.os.Process#SYSTEM_UID})
-     * <li> holds {@link Manifest.permission#MANAGE_COMPANION_DEVICES} and, if belongs to a
-     * different user, also holds {@link Manifest.permission#INTERACT_ACROSS_USERS}.
-     * </ul>
-     * @return whether the caller is one of the above.
-     */
-    public static boolean checkCallerCanManageAssociationsForPackage(@NonNull Context context,
-            @UserIdInt int userId, @NonNull String packageName) {
-        if (checkCallerIsSystemOr(userId, packageName)) return true;
-
-        if (!checkCallerCanInteractWithUserId(context, userId)) return false;
-
-        return checkCallerCanManageCompanionDevice(context);
-    }
-
     private static boolean checkPackage(@UserIdInt int uid, @NonNull String packageName) {
         try {
             return getAppOpsService().checkPackage(uid, packageName) == MODE_ALLOWED;
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 215f640..4a99007 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -29,6 +29,7 @@
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_RECENTS;
 import static android.content.pm.PackageManager.ACTION_REQUEST_PERMISSIONS;
 import static android.companion.virtualdevice.flags.Flags.virtualCameraServiceDiscovery;
+import static android.companion.virtualdevice.flags.Flags.intentInterceptionActionMatchingFix;
 
 import android.annotation.EnforcePermission;
 import android.annotation.NonNull;
@@ -1478,7 +1479,13 @@
         synchronized (mVirtualDeviceLock) {
             boolean hasInterceptedIntent = false;
             for (Map.Entry<IBinder, IntentFilter> interceptor : mIntentInterceptors.entrySet()) {
-                if (interceptor.getValue().match(
+                IntentFilter intentFilter = interceptor.getValue();
+                // Explicitly match the actions because the intent filter will match any intent
+                // without an explicit action. If the intent has no action, then require that there
+                // are no actions specified in the filter either.
+                boolean explicitActionMatch = !intentInterceptionActionMatchingFix()
+                        || intent.getAction() != null || intentFilter.countActions() == 0;
+                if (explicitActionMatch && intentFilter.match(
                         intent.getAction(), intent.getType(), intent.getScheme(), intent.getData(),
                         intent.getCategories(), TAG) >= 0) {
                     try {
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 0fdf6d0..f1339e9 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -232,7 +232,6 @@
         "android.hardware.rebootescrow-V1-java",
         "android.hardware.power.stats-V2-java",
         "android.hidl.manager-V1.2-java",
-        "audio-permission-aidl-java",
         "cbor-java",
         "com.android.media.audio-aconfig-java",
         "icu4j_calendar_astronomer",
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 4dd3a8f..b35959f 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3980,7 +3980,7 @@
                     if (resUuids.contains(rec.fsUuid)) continue;
 
                     // Treat as recent if mounted within the last week
-                    if (rec.lastSeenMillis > 0 && rec.lastSeenMillis < lastWeek) {
+                    if (rec.lastSeenMillis > 0 && rec.lastSeenMillis >= lastWeek) {
                         final StorageVolume userVol = rec.buildStorageVolume(mContext);
                         res.add(userVol);
                         resUuids.add(userVol.getUuid());
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 9be0e1f..51da7f17 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -499,8 +499,6 @@
     private final ServiceAnrTimer mShortFGSAnrTimer;
     // ActivityManagerConstants.DEFAULT_SERVICE_START_FOREGROUND_TIMEOUT_MS
     private final ServiceAnrTimer mServiceFGAnrTimer;
-    // see ServiceRecord#getEarliestStopTypeAndTime()
-    private final ServiceAnrTimer mFGSAnrTimer;
 
     /**
      * Mapping of uid to {fgs_type, fgs_info} for time limited fgs types such as dataSync and
@@ -784,9 +782,6 @@
         this.mServiceFGAnrTimer = new ServiceAnrTimer(service,
                 ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG,
                 "SERVICE_FOREGROUND_TIMEOUT");
-        this.mFGSAnrTimer = new ServiceAnrTimer(service,
-                ActivityManagerService.SERVICE_FGS_CRASH_TIMEOUT_MSG,
-                "FGS_TIMEOUT");
     }
 
     void systemServicesReady() {
@@ -3811,8 +3806,9 @@
 
             if (!sr.isFgsTimeLimited()) {
                 // Reset timers since new type does not have a timeout.
-                mFGSAnrTimer.cancel(sr);
                 mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_FGS_TIMEOUT_MSG, sr);
+                mAm.mHandler.removeMessages(
+                                ActivityManagerService.SERVICE_FGS_CRASH_TIMEOUT_MSG, sr);
                 return;
             }
         }
@@ -3834,9 +3830,9 @@
         }
         fgsTypeInfo.noteFgsFgsStart(nowUptime);
 
-        // We'll cancel the previous ANR timer and start a fresh one below.
-        mFGSAnrTimer.cancel(sr);
+        // We'll cancel the timeout and crash messages and post a fresh one below.
         mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_FGS_TIMEOUT_MSG, sr);
+        mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_FGS_CRASH_TIMEOUT_MSG, sr);
 
         final Message msg = mAm.mHandler.obtainMessage(
                 ActivityManagerService.SERVICE_FGS_TIMEOUT_MSG, sr);
@@ -3864,8 +3860,8 @@
             fgsTypeInfo.decNumParallelServices();
         }
         Slog.d(TAG_SERVICE, "Stop FGS timeout: " + sr);
-        mFGSAnrTimer.cancel(sr);
         mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_FGS_TIMEOUT_MSG, sr);
+        mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_FGS_CRASH_TIMEOUT_MSG, sr);
     }
 
     void onUidRemovedLocked(int uid) {
@@ -3892,7 +3888,8 @@
         synchronized (mAm) {
             final int fgsType = getTimeLimitedFgsType(sr.foregroundServiceType);
             if (fgsType == ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE || sr.app == null) {
-                mFGSAnrTimer.discard(sr);
+                mAm.mHandler.removeMessages(
+                                ActivityManagerService.SERVICE_FGS_CRASH_TIMEOUT_MSG, sr);
                 return;
             }
 
@@ -3901,8 +3898,9 @@
             final long nowUptime = SystemClock.uptimeMillis();
             if (lastTopTime != Long.MIN_VALUE && constantTimeLimit > (nowUptime - lastTopTime)) {
                 // Discard any other messages for this service
-                mFGSAnrTimer.discard(sr);
                 mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_FGS_TIMEOUT_MSG, sr);
+                mAm.mHandler.removeMessages(
+                                ActivityManagerService.SERVICE_FGS_CRASH_TIMEOUT_MSG, sr);
                 // The app was in the TOP state after the FGS was started so its time allowance
                 // should be counted from that time since this is considered a user interaction
                 final Message msg = mAm.mHandler.obtainMessage(
@@ -3913,7 +3911,6 @@
 
             Slog.e(TAG_SERVICE, "FGS (" + ServiceInfo.foregroundServiceTypeToLabel(fgsType)
                     + ") timed out: " + sr);
-            mFGSAnrTimer.accept(sr);
             traceInstant("FGS timed out: ", sr);
 
             final TimeLimitedFgsInfo fgsTypeInfo = getFgsTimeLimitedInfo(sr.appInfo.uid, fgsType);
@@ -3940,7 +3937,9 @@
             }
 
             // Crash the service after giving the service some time to clean up.
-            mFGSAnrTimer.start(sr, mAm.mConstants.mFgsCrashExtraWaitDuration);
+            final Message msg = mAm.mHandler.obtainMessage(
+                                    ActivityManagerService.SERVICE_FGS_CRASH_TIMEOUT_MSG, sr);
+            mAm.mHandler.sendMessageDelayed(msg, mAm.mConstants.mFgsCrashExtraWaitDuration);
         }
     }
 
@@ -3963,8 +3962,9 @@
                 + ServiceInfo.foregroundServiceTypeToLabel(fgsType)
                 + " did not stop within its timeout: " + sr.getComponentName();
 
-        if (android.app.Flags.gateFgsTimeoutAnrBehavior()) {
-            // Log a WTF instead of throwing an ANR while the new behavior is gated.
+        if (android.app.Flags.gateFgsTimeoutAnrBehavior()
+                || !android.app.Flags.enableFgsTimeoutCrashBehavior()) {
+            // Log a WTF instead of crashing the app while the new behavior is gated.
             Slog.wtf(TAG, reason);
             return;
         }
@@ -3981,23 +3981,6 @@
                                     .createExtrasForService(sr.getComponentName()));
                 }
             }
-        } else {
-            // ANR the app if the new crash behavior is not enabled
-            final TimeoutRecord tr = TimeoutRecord.forFgsTimeout(reason);
-            tr.mLatencyTracker.waitingOnAMSLockStarted();
-            synchronized (mAm) {
-                tr.mLatencyTracker.waitingOnAMSLockEnded();
-
-                Slog.e(TAG_SERVICE, "FGS ANR'ed: " + sr);
-                traceInstant("FGS ANR: ", sr);
-                if (sr.app != null) {
-                    mAm.appNotResponding(sr.app, tr);
-                }
-
-                // TODO: Can we close the ANR dialog here, if it's still shown? Currently, the ANR
-                // dialog really doesn't remember the "cause" (especially if there have been
-                // multiple ANRs), so it's not doable.
-            }
         }
     }
 
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index db4840d..211f952 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -83,8 +83,6 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.ServiceThread;
 
-import dalvik.annotation.optimization.NeverCompile;
-
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -100,6 +98,8 @@
 import java.util.Random;
 import java.util.Set;
 
+import dalvik.annotation.optimization.NeverCompile;
+
 public final class CachedAppOptimizer {
 
     // Flags stored in the DeviceConfig API.
@@ -2633,7 +2633,7 @@
     public void binderError(int debugPid, ProcessRecord app, int code, int flags, int err) {
         Slog.w(TAG_AM, "pid " + debugPid + " " + (app == null ? "null" : app.processName)
                 + " sent binder code " + code + " with flags " + flags
-                + " and got error " + err);
+                + " to frozen apps and got error " + err);
 
         // Do nothing if the binder error callback is not enabled.
         // That means the frozen apps in a wrong state will be killed when they are unfrozen later.
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 8647750..ab34dd4 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -2205,12 +2205,15 @@
                                     != 0 ? PROCESS_CAPABILITY_FOREGROUND_LOCATION : 0;
 
                     if (roForegroundAudioControl()) { // flag check
-                        final int fgsAudioType = FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
-                                | FOREGROUND_SERVICE_TYPE_CAMERA
-                                | FOREGROUND_SERVICE_TYPE_MICROPHONE
-                                | FOREGROUND_SERVICE_TYPE_PHONE_CALL;
-                        capabilityFromFGS |= (psr.getForegroundServiceTypes() & fgsAudioType) != 0
-                                ? PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL : 0;
+                        // TODO revisit restriction of FOREGROUND_AUDIO_CONTROL when it can be
+                        //      limited to specific FGS types
+                        //final int fgsAudioType = FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK
+                        //        | FOREGROUND_SERVICE_TYPE_CAMERA
+                        //        | FOREGROUND_SERVICE_TYPE_MICROPHONE
+                        //        | FOREGROUND_SERVICE_TYPE_PHONE_CALL;
+                        //capabilityFromFGS |= (psr.getForegroundServiceTypes() & fgsAudioType) != 0
+                        //        ? PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL : 0;
+                        capabilityFromFGS |= PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL;
                     }
 
                     final boolean enabled = state.getCachedCompatChange(
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 8d7a1c9..8eef71e 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -22,6 +22,8 @@
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_COMPAT;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
+import static android.os.Process.ROOT_UID;
+import static android.os.Process.SYSTEM_UID;
 
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -422,6 +424,10 @@
             })
     public static BackgroundStartPrivileges getDefaultBackgroundStartPrivileges(
             int callingUid, @Nullable String callingPackage) {
+        if (callingUid == ROOT_UID || callingUid == SYSTEM_UID) {
+            // root and system must always opt in explicitly
+            return BackgroundStartPrivileges.ALLOW_FGS;
+        }
         boolean isChangeEnabledForApp = callingPackage != null ? CompatChanges.isChangeEnabled(
                 DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_SENDER, callingPackage,
                 UserHandle.getUserHandleForUid(callingUid)) : CompatChanges.isChangeEnabled(
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 2184340..b9cdf27 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -1005,7 +1005,7 @@
         if (isForeground || foregroundId != 0) {
             pw.print(prefix); pw.print("isForeground="); pw.print(isForeground);
             pw.print(" foregroundId="); pw.print(foregroundId);
-            pw.printf(" types=%08X", foregroundServiceType);
+            pw.printf(" types=0x%08X", foregroundServiceType);
             pw.print(" foregroundNoti="); pw.println(foregroundNoti);
 
             if (isShortFgs() && mShortFgsInfo != null) {
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 1dc1846..1d21ccb 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -15,6 +15,8 @@
  */
 package com.android.server.audio;
 
+import static android.media.audio.Flags.scoManagedByAudio;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.compat.CompatChanges;
@@ -54,6 +56,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.sysprop.BluetoothProperties;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
@@ -74,7 +77,6 @@
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-
 /**
  * @hide
  * (non final for mocking/spying)
@@ -167,6 +169,15 @@
     @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S_V2)
     public static final long USE_SET_COMMUNICATION_DEVICE = 243827847L;
 
+    /** Indicates if headset profile connection and SCO audio control use the new implementation
+     * aligned with other BT profiles. True if both the feature flag Flags.scoManagedByAudio() and
+     * the system property audio.sco.managed.by.audio are true.
+     */
+    private final boolean mScoManagedByAudio;
+    /*package*/ boolean isScoManagedByAudio() {
+        return mScoManagedByAudio;
+    }
+
     //-------------------------------------------------------------------
     /*package*/ AudioDeviceBroker(@NonNull Context context, @NonNull AudioService service,
             @NonNull AudioSystemAdapter audioSystem) {
@@ -176,7 +187,8 @@
         mDeviceInventory = new AudioDeviceInventory(this);
         mSystemServer = SystemServerAdapter.getDefaultAdapter(mContext);
         mAudioSystem = audioSystem;
-
+        mScoManagedByAudio = scoManagedByAudio()
+                && BluetoothProperties.isScoManagedByAudioEnabled().orElse(false);
         init();
     }
 
@@ -192,7 +204,8 @@
         mDeviceInventory = mockDeviceInventory;
         mSystemServer = mockSystemServer;
         mAudioSystem = audioSystem;
-
+        mScoManagedByAudio = scoManagedByAudio()
+                && BluetoothProperties.isScoManagedByAudioEnabled().orElse(false);
         init();
     }
 
@@ -400,24 +413,24 @@
         if (client == null) {
             return;
         }
-
-        boolean isBtScoRequested = isBluetoothScoRequested();
-        if (isBtScoRequested && (!wasBtScoRequested || !isBluetoothScoActive())) {
-            if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) {
-                Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for uid: "
-                        + uid);
-                // clean up or restore previous client selection
-                if (prevClientDevice != null) {
-                    addCommunicationRouteClient(cb, uid, prevClientDevice, prevPrivileged);
-                } else {
-                    removeCommunicationRouteClient(cb, true);
+        if (!mScoManagedByAudio) {
+            boolean isBtScoRequested = isBluetoothScoRequested();
+            if (isBtScoRequested && (!wasBtScoRequested || !isBluetoothScoActive())) {
+                if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) {
+                    Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for uid: "
+                            + uid);
+                    // clean up or restore previous client selection
+                    if (prevClientDevice != null) {
+                        addCommunicationRouteClient(cb, uid, prevClientDevice, prevPrivileged);
+                    } else {
+                        removeCommunicationRouteClient(cb, true);
+                    }
+                    postBroadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
                 }
-                postBroadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+            } else if (!isBtScoRequested && wasBtScoRequested) {
+                mBtHelper.stopBluetoothSco(eventSource);
             }
-        } else if (!isBtScoRequested && wasBtScoRequested) {
-            mBtHelper.stopBluetoothSco(eventSource);
         }
-
         // In BT classic for communication, the device changes from a2dp to sco device, but for
         // LE Audio it stays the same and we must trigger the proper stream volume alignment, if
         // LE Audio communication device is activated after the audio system has already switched to
@@ -1685,6 +1698,8 @@
 
         pw.println("\n" + prefix + "mAudioModeOwner: " + mAudioModeOwner);
 
+        pw.println("\n" + prefix + "mScoManagedByAudio: " + mScoManagedByAudio);
+
         mBtHelper.dump(pw, prefix);
     }
 
@@ -1837,10 +1852,10 @@
                                             ? mAudioService.getBluetoothContextualVolumeStream()
                                             : AudioSystem.STREAM_DEFAULT);
                                 if (btInfo.mProfile == BluetoothProfile.LE_AUDIO
-                                        || btInfo.mProfile
-                                        == BluetoothProfile.HEARING_AID) {
-                                    onUpdateCommunicationRouteClient(
-                                            isBluetoothScoRequested(),
+                                        || btInfo.mProfile == BluetoothProfile.HEARING_AID
+                                        || (mScoManagedByAudio
+                                            && btInfo.mProfile == BluetoothProfile.HEADSET)) {
+                                    onUpdateCommunicationRouteClient(isBluetoothScoRequested(),
                                             "setBluetoothActiveDevice");
                                 }
                             }
@@ -2511,7 +2526,7 @@
             setCommunicationRouteForClient(crc.getBinder(), crc.getUid(), crc.getDevice(),
                     BtHelper.SCO_MODE_UNDEFINED, crc.isPrivileged(), eventSource);
         } else {
-            if (!isBluetoothScoRequested() && wasBtScoRequested) {
+            if (!mScoManagedByAudio && !isBluetoothScoRequested() && wasBtScoRequested) {
                 mBtHelper.stopBluetoothSco(eventSource);
             }
             updateCommunicationRoute(eventSource);
@@ -2815,4 +2830,5 @@
     void clearDeviceInventory() {
         mDeviceInventory.clearDeviceInventory();
     }
+
 }
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index c9612ca..287c92f 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -293,6 +293,7 @@
             Iterator<Entry<Pair<Integer, String>, AdiDeviceState>> iterator =
                     mDeviceInventory.entrySet().iterator();
             if (iterator.hasNext()) {
+                iterator.next();
                 iterator.remove();
             }
         }
@@ -858,6 +859,15 @@
                                 btInfo, streamType, codec, "onSetBtActiveDevice");
                     }
                     break;
+                case BluetoothProfile.HEADSET:
+                    if (mDeviceBroker.isScoManagedByAudio()) {
+                        if (switchToUnavailable) {
+                            mDeviceBroker.onSetBtScoActiveDevice(null);
+                        } else if (switchToAvailable) {
+                            mDeviceBroker.onSetBtScoActiveDevice(btInfo.mDevice);
+                        }
+                    }
+                    break;
                 default: throw new IllegalArgumentException("Invalid profile "
                                  + BluetoothProfile.getProfileName(btInfo.mProfile));
             }
diff --git a/services/core/java/com/android/server/audio/AudioPolicyFacade.java b/services/core/java/com/android/server/audio/AudioPolicyFacade.java
index 02e80d6..f652b33 100644
--- a/services/core/java/com/android/server/audio/AudioPolicyFacade.java
+++ b/services/core/java/com/android/server/audio/AudioPolicyFacade.java
@@ -16,12 +16,14 @@
 
 package com.android.server.audio;
 
+import com.android.media.permission.INativePermissionController;
 
 /**
  * Facade to IAudioPolicyService which fulfills AudioService dependencies.
  * See @link{IAudioPolicyService.aidl}
  */
 public interface AudioPolicyFacade {
-
     public boolean isHotwordStreamSupported(boolean lookbackAudio);
+    public INativePermissionController getPermissionController();
+    public void registerOnStartTask(Runnable r);
 }
diff --git a/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java b/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java
new file mode 100644
index 0000000..5ea3c4b
--- /dev/null
+++ b/services/core/java/com/android/server/audio/AudioServerPermissionProvider.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.audio;
+
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.media.permission.INativePermissionController;
+import com.android.media.permission.UidPackageState;
+import com.android.server.pm.pkg.PackageState;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+
+/** Responsible for synchronizing system server permission state to the native audioserver. */
+public class AudioServerPermissionProvider {
+
+    private final Object mLock = new Object();
+
+    @GuardedBy("mLock")
+    private INativePermissionController mDest;
+
+    @GuardedBy("mLock")
+    private final Map<Integer, Set<String>> mPackageMap;
+
+    /**
+     * @param appInfos - PackageState for all apps on the device, used to populate init state
+     */
+    public AudioServerPermissionProvider(Collection<PackageState> appInfos) {
+        // Initialize the package state
+        mPackageMap = generatePackageMappings(appInfos);
+    }
+
+    /**
+     * Called whenever audioserver starts (or started before us)
+     *
+     * @param pc - The permission controller interface from audioserver, which we push updates to
+     */
+    public void onServiceStart(@Nullable INativePermissionController pc) {
+        if (pc == null) return;
+        synchronized (mLock) {
+            mDest = pc;
+            resetNativePackageState();
+        }
+    }
+
+    /**
+     * Called when a package is added or removed
+     *
+     * @param uid - uid of modified package (only app-id matters)
+     * @param packageName - the (new) packageName
+     * @param isRemove - true if the package is being removed, false if it is being added
+     */
+    public void onModifyPackageState(int uid, String packageName, boolean isRemove) {
+        // No point in maintaining package mappings for uids of different users
+        uid = UserHandle.getAppId(uid);
+        synchronized (mLock) {
+            // Update state
+            Set<String> packages;
+            if (!isRemove) {
+                packages = mPackageMap.computeIfAbsent(uid, unused -> new ArraySet(1));
+                packages.add(packageName);
+            } else {
+                packages = mPackageMap.get(uid);
+                if (packages != null) {
+                    packages.remove(packageName);
+                    if (packages.isEmpty()) mPackageMap.remove(uid);
+                }
+            }
+            // Push state to destination
+            if (mDest == null) {
+                return;
+            }
+            var state = new UidPackageState();
+            state.uid = uid;
+            state.packageNames = packages != null ? List.copyOf(packages) : Collections.emptyList();
+            try {
+                mDest.updatePackagesForUid(state);
+            } catch (RemoteException e) {
+                // We will re-init the state when the service comes back up
+                mDest = null;
+            }
+        }
+    }
+
+    /** Called when full syncing package state to audioserver. */
+    @GuardedBy("mLock")
+    private void resetNativePackageState() {
+        if (mDest == null) return;
+        List<UidPackageState> states =
+                mPackageMap.entrySet().stream()
+                        .map(
+                                entry -> {
+                                    UidPackageState state = new UidPackageState();
+                                    state.uid = entry.getKey();
+                                    state.packageNames = List.copyOf(entry.getValue());
+                                    return state;
+                                })
+                        .toList();
+        try {
+            mDest.populatePackagesForUids(states);
+        } catch (RemoteException e) {
+            // We will re-init the state when the service comes back up
+            mDest = null;
+        }
+    }
+
+    /**
+     * Aggregation operation on all package states list: groups by states by app-id and merges the
+     * packageName for each state into an ArraySet.
+     */
+    private static Map<Integer, Set<String>> generatePackageMappings(
+            Collection<PackageState> appInfos) {
+        Collector<PackageState, Object, Set<String>> reducer =
+                Collectors.mapping(
+                        (PackageState p) -> p.getPackageName(),
+                        Collectors.toCollection(() -> new ArraySet(1)));
+
+        return appInfos.stream()
+                .collect(
+                        Collectors.groupingBy(
+                                /* predicate */ (PackageState p) -> p.getAppId(),
+                                /* factory */ HashMap::new,
+                                /* downstream collector */ reducer));
+    }
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 72c5254..ef65b25 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -16,8 +16,25 @@
 
 package com.android.server.audio;
 
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+import static android.Manifest.permission.BLUETOOTH_STACK;
+import static android.Manifest.permission.CALL_AUDIO_INTERCEPTION;
+import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD;
+import static android.Manifest.permission.CAPTURE_AUDIO_OUTPUT;
+import static android.Manifest.permission.CAPTURE_MEDIA_OUTPUT;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.MODIFY_AUDIO_ROUTING;
+import static android.Manifest.permission.MODIFY_AUDIO_SETTINGS;
 import static android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED;
+import static android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS;
+import static android.Manifest.permission.MODIFY_PHONE_STATE;
+import static android.Manifest.permission.QUERY_AUDIO_STATE;
+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_BLE_HEADSET;
 import static android.media.AudioDeviceInfo.TYPE_BLE_SPEAKER;
 import static android.media.AudioDeviceInfo.TYPE_BLUETOOTH_A2DP;
@@ -35,6 +52,7 @@
 import static android.media.audio.Flags.featureSpatialAudioHeadtrackingLowLatency;
 import static android.media.audio.Flags.focusFreezeTestApi;
 import static android.media.audio.Flags.roForegroundAudioControl;
+import static android.media.audio.Flags.scoManagedByAudio;
 import static android.media.audiopolicy.Flags.enableFadeManagerConfiguration;
 import static android.os.Process.FIRST_APPLICATION_UID;
 import static android.os.Process.INVALID_UID;
@@ -43,7 +61,9 @@
 import static android.provider.Settings.Secure.VOLUME_HUSH_VIBRATE;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+import static com.android.media.audio.Flags.absVolumeIndexFix;
 import static com.android.media.audio.Flags.alarmMinVolumeZero;
+import static com.android.media.audio.Flags.audioserverPermissions;
 import static com.android.media.audio.Flags.disablePrescaleAbsoluteVolume;
 import static com.android.media.audio.Flags.ringerModeAffectsAlarm;
 import static com.android.media.audio.Flags.setStreamVolumeOrder;
@@ -225,15 +245,18 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.Preconditions;
 import com.android.server.EventLogTags;
+import com.android.server.LocalManagerRegistry;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.audio.AudioServiceEvents.DeviceVolumeEvent;
 import com.android.server.audio.AudioServiceEvents.PhoneStateEvent;
 import com.android.server.audio.AudioServiceEvents.VolChangedBroadcastEvent;
 import com.android.server.audio.AudioServiceEvents.VolumeEvent;
+import com.android.server.pm.PackageManagerLocal;
 import com.android.server.pm.UserManagerInternal;
 import com.android.server.pm.UserManagerInternal.UserRestrictionsListener;
 import com.android.server.pm.UserManagerService;
+import com.android.server.pm.pkg.PackageState;
 import com.android.server.utils.EventLogger;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
@@ -258,6 +281,7 @@
 import java.util.Set;
 import java.util.TreeSet;
 import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.BooleanSupplier;
@@ -288,6 +312,8 @@
     private final SettingsAdapter mSettings;
     private final AudioPolicyFacade mAudioPolicy;
 
+    private final AudioServerPermissionProvider mPermissionProvider;
+
     private final MusicFxHelper mMusicFxHelper;
 
     /** Debug audio mode */
@@ -618,6 +644,17 @@
     // If absolute volume is supported in AVRCP device
     private volatile boolean mAvrcpAbsVolSupported = false;
 
+    private final Object mCachedAbsVolDrivingStreamsLock = new Object();
+    // Contains for all the device types which support absolute volume the current streams that
+    // are driving the volume changes
+    @GuardedBy("mCachedAbsVolDrivingStreamsLock")
+    private final HashMap<Integer, Integer> mCachedAbsVolDrivingStreams = new HashMap<>(
+            Map.of(AudioSystem.DEVICE_OUT_BLE_HEADSET, AudioSystem.STREAM_MUSIC,
+                    AudioSystem.DEVICE_OUT_BLE_SPEAKER, AudioSystem.STREAM_MUSIC,
+                    AudioSystem.DEVICE_OUT_BLE_BROADCAST, AudioSystem.STREAM_MUSIC,
+                    AudioSystem.DEVICE_OUT_HEARING_AID, AudioSystem.STREAM_MUSIC
+            ));
+
     /**
     * Default stream type used for volume control in the absence of playback
     * e.g. user on homescreen, no app playing anything, presses hardware volume buttons, this
@@ -995,14 +1032,22 @@
 
         public Lifecycle(Context context) {
             super(context);
+            var audioserverLifecycleExecutor = Executors.newSingleThreadExecutor();
+            var audioPolicyFacade = new DefaultAudioPolicyFacade(audioserverLifecycleExecutor);
             mService = new AudioService(context,
                               AudioSystemAdapter.getDefaultAdapter(),
                               SystemServerAdapter.getDefaultAdapter(context),
                               SettingsAdapter.getDefaultAdapter(),
                               new AudioVolumeGroupHelper(),
-                              new DefaultAudioPolicyFacade(),
-                              null);
-
+                              audioPolicyFacade,
+                              null,
+                              context.getSystemService(AppOpsManager.class),
+                              PermissionEnforcer.fromContext(context),
+                              audioserverPermissions() ?
+                                initializeAudioServerPermissionProvider(
+                                    context, audioPolicyFacade, audioserverLifecycleExecutor) :
+                                    null
+                              );
         }
 
         @Override
@@ -1079,25 +1124,6 @@
     /**
      * @param context
      * @param audioSystem Adapter for {@link AudioSystem}
-     * @param systemServer Adapter for privileged functionality for system server components
-     * @param settings Adapter for {@link Settings}
-     * @param audioVolumeGroupHelper Adapter for {@link AudioVolumeGroup}
-     * @param audioPolicy Interface of a facade to IAudioPolicyManager
-     * @param looper Looper to use for the service's message handler. If this is null, an
-     *               {@link AudioSystemThread} is created as the messaging thread instead.
-     */
-    public AudioService(Context context, AudioSystemAdapter audioSystem,
-            SystemServerAdapter systemServer, SettingsAdapter settings,
-            AudioVolumeGroupHelperBase audioVolumeGroupHelper, AudioPolicyFacade audioPolicy,
-            @Nullable Looper looper) {
-        this (context, audioSystem, systemServer, settings, audioVolumeGroupHelper,
-                audioPolicy, looper, context.getSystemService(AppOpsManager.class),
-                PermissionEnforcer.fromContext(context));
-    }
-
-    /**
-     * @param context
-     * @param audioSystem Adapter for {@link AudioSystem}
      * @param systemServer Adapter for privilieged functionality for system server components
      * @param settings Adapter for {@link Settings}
      * @param audioVolumeGroupHelper Adapter for {@link AudioVolumeGroup}
@@ -1111,13 +1137,16 @@
     public AudioService(Context context, AudioSystemAdapter audioSystem,
             SystemServerAdapter systemServer, SettingsAdapter settings,
             AudioVolumeGroupHelperBase audioVolumeGroupHelper, AudioPolicyFacade audioPolicy,
-            @Nullable Looper looper, AppOpsManager appOps, @NonNull PermissionEnforcer enforcer) {
+            @Nullable Looper looper, AppOpsManager appOps, @NonNull PermissionEnforcer enforcer,
+            /* @NonNull */ AudioServerPermissionProvider permissionProvider) {
         super(enforcer);
         sLifecycleLogger.enqueue(new EventLogger.StringEvent("AudioService()"));
         mContext = context;
         mContentResolver = context.getContentResolver();
         mAppOps = appOps;
 
+        mPermissionProvider = permissionProvider;
+
         mAudioSystem = audioSystem;
         mSystemServer = systemServer;
         mAudioVolumeGroupHelper = audioVolumeGroupHelper;
@@ -1458,6 +1487,13 @@
 
         // check on volume initialization
         checkVolumeRangeInitialization("AudioService()");
+
+        synchronized (mCachedAbsVolDrivingStreamsLock) {
+            mCachedAbsVolDrivingStreams.forEach((dev, stream) -> {
+                mAudioSystem.setDeviceAbsoluteVolumeEnabled(dev, /*address=*/"", /*enabled=*/true,
+                        stream);
+            });
+        }
     }
 
     private SubscriptionManager.OnSubscriptionsChangedListener mSubscriptionChangedListener =
@@ -1490,7 +1526,9 @@
         // Register for device connection intent broadcasts.
         IntentFilter intentFilter =
                 new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
-        intentFilter.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
+        if (!mDeviceBroker.isScoManagedByAudio()) {
+            intentFilter.addAction(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED);
+        }
         intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
         if (mDisplayManager == null) {
             intentFilter.addAction(Intent.ACTION_SCREEN_ON);
@@ -1888,7 +1926,6 @@
         }
 
         mSpatializerHelper.reset(/* featureEnabled */ mHasSpatializerEffect);
-        mSoundDoseHelper.reset();
 
         // Restore rotation information.
         if (mMonitorRotation) {
@@ -1896,9 +1933,19 @@
         }
 
         onIndicateSystemReady();
+
+        synchronized (mCachedAbsVolDrivingStreamsLock) {
+            mCachedAbsVolDrivingStreams.forEach((dev, stream) -> {
+                mAudioSystem.setDeviceAbsoluteVolumeEnabled(dev, /*address=*/"", /*enabled=*/true,
+                        stream);
+            });
+        }
+
         // indicate the end of reconfiguration phase to audio HAL
         AudioSystem.setParameters("restarting=false");
 
+        mSoundDoseHelper.reset(/*resetISoundDose=*/true);
+
         sendMsg(mAudioHandler, MSG_DISPATCH_AUDIO_SERVER_STATE,
                 SENDMSG_QUEUE, 1, 0, null, 0);
 
@@ -2075,7 +2122,7 @@
         }
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     /**
      * @see AudioManager#setSupportedSystemUsages(int[])
      */
@@ -2090,7 +2137,7 @@
         }
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     /**
      * @see AudioManager#getSupportedSystemUsages()
      */
@@ -2110,7 +2157,7 @@
         }
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     /**
      * @return the {@link android.media.audiopolicy.AudioProductStrategy} discovered from the
      * platform configuration file.
@@ -2124,9 +2171,7 @@
     }
 
     @android.annotation.EnforcePermission(anyOf = {
-            MODIFY_AUDIO_SETTINGS_PRIVILEGED,
-            android.Manifest.permission.MODIFY_AUDIO_ROUTING
-    })
+            MODIFY_AUDIO_SETTINGS_PRIVILEGED, MODIFY_AUDIO_ROUTING })
     /**
      * @return the List of {@link android.media.audiopolicy.AudioVolumeGroup} discovered from the
      * platform configuration file.
@@ -2584,7 +2629,7 @@
             Log.w(TAG, "audioFormat to enable is not a surround format.");
             return false;
         }
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.WRITE_SETTINGS)
+        if (mContext.checkCallingOrSelfPermission(WRITE_SETTINGS)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Missing WRITE_SETTINGS permission");
         }
@@ -2608,7 +2653,7 @@
         return true;
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.WRITE_SETTINGS)
+    @android.annotation.EnforcePermission(WRITE_SETTINGS)
     /** @see AudioManager#setEncodedSurroundMode(int) */
     @Override
     public boolean setEncodedSurroundMode(@AudioManager.EncodedSurroundOutputMode int mode) {
@@ -2786,7 +2831,7 @@
         if (!TextUtils.isEmpty(packageName)) {
             PackageManager pm = mContext.getPackageManager();
 
-            if (pm.checkPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD, packageName)
+            if (pm.checkPermission(CAPTURE_AUDIO_HOTWORD, packageName)
                     == PackageManager.PERMISSION_GRANTED) {
                 try {
                     assistantUid = pm.getPackageUidAsUser(packageName, getCurrentUserId());
@@ -2973,7 +3018,7 @@
      * @see AudioManager#setPreferredDevicesForStrategy(AudioProductStrategy,
      *                                                  List<AudioDeviceAttributes>)
      */
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     public int setPreferredDevicesForStrategy(int strategy, List<AudioDeviceAttributes> devices) {
         super.setPreferredDevicesForStrategy_enforcePermission();
         if (devices == null) {
@@ -3001,7 +3046,7 @@
         return status;
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     /** @see AudioManager#removePreferredDeviceForStrategy(AudioProductStrategy) */
     public int removePreferredDevicesForStrategy(int strategy) {
         super.removePreferredDevicesForStrategy_enforcePermission();
@@ -3017,7 +3062,7 @@
         return status;
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     /**
      * @see AudioManager#getPreferredDeviceForStrategy(AudioProductStrategy)
      * @see AudioManager#getPreferredDevicesForStrategy(AudioProductStrategy)
@@ -3049,7 +3094,7 @@
      * @see AudioManager#setDeviceAsNonDefaultForStrategy(AudioProductStrategy,
      *                                                     List<AudioDeviceAttributes>)
      */
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     public int setDeviceAsNonDefaultForStrategy(int strategy,
                                                 @NonNull AudioDeviceAttributes device) {
         super.setDeviceAsNonDefaultForStrategy_enforcePermission();
@@ -3078,7 +3123,7 @@
      * @see AudioManager#removeDeviceAsNonDefaultForStrategy(AudioProductStrategy,
      *                                                       AudioDeviceAttributes)
      */
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     public int removeDeviceAsNonDefaultForStrategy(int strategy,
                                                    AudioDeviceAttributes device) {
         super.removeDeviceAsNonDefaultForStrategy_enforcePermission();
@@ -3104,7 +3149,7 @@
     /**
      * @see AudioManager#getNonDefaultDevicesForStrategy(AudioProductStrategy)
      */
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     public List<AudioDeviceAttributes> getNonDefaultDevicesForStrategy(int strategy) {
         super.getNonDefaultDevicesForStrategy_enforcePermission();
         List<AudioDeviceAttributes> devices = new ArrayList<>();
@@ -3205,7 +3250,7 @@
         return status;
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     /** @see AudioManager#clearPreferredDevicesForCapturePreset(int) */
     public int clearPreferredDevicesForCapturePreset(int capturePreset) {
         super.clearPreferredDevicesForCapturePreset_enforcePermission();
@@ -3221,7 +3266,7 @@
         return status;
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     /**
      * @see AudioManager#getPreferredDevicesForCapturePreset(int)
      */
@@ -3555,7 +3600,7 @@
         if (isMuteAdjust &&
             (streamType == AudioSystem.STREAM_VOICE_CALL ||
                 streamType == AudioSystem.STREAM_BLUETOOTH_SCO) &&
-                mContext.checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
+                mContext.checkPermission(MODIFY_PHONE_STATE, pid, uid)
                     != PackageManager.PERMISSION_GRANTED) {
             Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: adjustStreamVolume from pid="
                     + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
@@ -3566,7 +3611,7 @@
         // make sure that the calling app have the MODIFY_AUDIO_ROUTING permission.
         if (streamType == AudioSystem.STREAM_ASSISTANT &&
                 mContext.checkPermission(
-                android.Manifest.permission.MODIFY_AUDIO_ROUTING, pid, uid)
+                MODIFY_AUDIO_ROUTING, pid, uid)
                     != PackageManager.PERMISSION_GRANTED) {
             Log.w(TAG, "MODIFY_AUDIO_ROUTING Permission Denial: adjustStreamVolume from pid="
                     + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
@@ -3722,8 +3767,10 @@
 
             int newIndex = mStreamStates[streamType].getIndex(device);
 
+            int streamToDriveAbsVol = absVolumeIndexFix() ? getBluetoothContextualVolumeStream() :
+                    AudioSystem.STREAM_MUSIC;
             // Check if volume update should be send to AVRCP
-            if (streamTypeAlias == AudioSystem.STREAM_MUSIC
+            if (streamTypeAlias == streamToDriveAbsVol
                     && AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
                     && (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
                 if (DEBUG_VOL) {
@@ -3997,50 +4044,25 @@
     }
 
     private void enforceModifyAudioRoutingPermission() {
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+        if (mContext.checkCallingOrSelfPermission(MODIFY_AUDIO_ROUTING)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Missing MODIFY_AUDIO_ROUTING permission");
         }
     }
 
-    private void enforceAccessUltrasoundPermission() {
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.ACCESS_ULTRASOUND)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Missing ACCESS_ULTRASOUND permission");
-        }
-    }
-
-    private void enforceQueryStatePermission() {
-        if (mContext.checkCallingOrSelfPermission(Manifest.permission.QUERY_AUDIO_STATE)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Missing QUERY_AUDIO_STATE permissions");
-        }
-    }
-
     private void enforceQueryStateOrModifyRoutingPermission() {
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+        if (mContext.checkCallingOrSelfPermission(MODIFY_AUDIO_ROUTING)
                 != PackageManager.PERMISSION_GRANTED
-                && mContext.checkCallingOrSelfPermission(Manifest.permission.QUERY_AUDIO_STATE)
+                && mContext.checkCallingOrSelfPermission(QUERY_AUDIO_STATE)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException(
                     "Missing MODIFY_AUDIO_ROUTING or QUERY_AUDIO_STATE permissions");
         }
     }
 
-    private void enforceCallAudioInterceptionPermission() {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.CALL_AUDIO_INTERCEPTION)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Missing CALL_AUDIO_INTERCEPTION permission");
-        }
-    }
-
-
     @Override
     @android.annotation.EnforcePermission(anyOf = {
-            MODIFY_AUDIO_SETTINGS_PRIVILEGED,
-            android.Manifest.permission.MODIFY_AUDIO_ROUTING
-    })
+            MODIFY_AUDIO_SETTINGS_PRIVILEGED, MODIFY_AUDIO_ROUTING })
     /** @see AudioManager#setVolumeGroupVolumeIndex(int, int, int) */
     public void setVolumeGroupVolumeIndex(int groupId, int index, int flags,
             String callingPackage, String attributionTag) {
@@ -4074,9 +4096,7 @@
 
     @Override
     @android.annotation.EnforcePermission(anyOf = {
-            MODIFY_AUDIO_SETTINGS_PRIVILEGED,
-            android.Manifest.permission.MODIFY_AUDIO_ROUTING
-    })
+            MODIFY_AUDIO_SETTINGS_PRIVILEGED, MODIFY_AUDIO_ROUTING })
     /** @see AudioManager#getVolumeGroupVolumeIndex(int) */
     public int getVolumeGroupVolumeIndex(int groupId) {
         super.getVolumeGroupVolumeIndex_enforcePermission();
@@ -4093,9 +4113,7 @@
 
     /** @see AudioManager#getVolumeGroupMaxVolumeIndex(int) */
     @android.annotation.EnforcePermission(anyOf = {
-            MODIFY_AUDIO_SETTINGS_PRIVILEGED,
-            android.Manifest.permission.MODIFY_AUDIO_ROUTING
-    })
+            MODIFY_AUDIO_SETTINGS_PRIVILEGED, MODIFY_AUDIO_ROUTING })
     public int getVolumeGroupMaxVolumeIndex(int groupId) {
         super.getVolumeGroupMaxVolumeIndex_enforcePermission();
         synchronized (VolumeStreamState.class) {
@@ -4109,9 +4127,7 @@
 
     /** @see AudioManager#getVolumeGroupMinVolumeIndex(int) */
     @android.annotation.EnforcePermission(anyOf = {
-            MODIFY_AUDIO_SETTINGS_PRIVILEGED,
-            android.Manifest.permission.MODIFY_AUDIO_ROUTING
-    })
+            MODIFY_AUDIO_SETTINGS_PRIVILEGED, MODIFY_AUDIO_ROUTING })
     public int getVolumeGroupMinVolumeIndex(int groupId) {
         super.getVolumeGroupMinVolumeIndex_enforcePermission();
         synchronized (VolumeStreamState.class) {
@@ -4125,9 +4141,7 @@
 
     @Override
     @android.annotation.EnforcePermission(anyOf = {
-            android.Manifest.permission.MODIFY_AUDIO_ROUTING,
-            android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
-    })
+            MODIFY_AUDIO_ROUTING, MODIFY_AUDIO_SETTINGS_PRIVILEGED })
     /** @see AudioDeviceVolumeManager#setDeviceVolume(VolumeInfo, AudioDeviceAttributes)
      * Part of service interface, check permissions and parameters here
      * Note calling package is for logging purposes only, not to be trusted
@@ -4253,7 +4267,7 @@
     }
 
     /** @see AudioManager#getLastAudibleVolumeForVolumeGroup(int) */
-    @android.annotation.EnforcePermission(android.Manifest.permission.QUERY_AUDIO_STATE)
+    @android.annotation.EnforcePermission(QUERY_AUDIO_STATE)
     public int getLastAudibleVolumeForVolumeGroup(int groupId) {
         super.getLastAudibleVolumeForVolumeGroup_enforcePermission();
         synchronized (VolumeStreamState.class) {
@@ -4318,16 +4332,14 @@
             return;
         }
         if ((streamType == AudioManager.STREAM_VOICE_CALL) && (index == 0)
-                && (mContext.checkCallingOrSelfPermission(
-                    android.Manifest.permission.MODIFY_PHONE_STATE)
+                && (mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
                     != PackageManager.PERMISSION_GRANTED)) {
             Log.w(TAG, "Trying to call setStreamVolume() for STREAM_VOICE_CALL and index 0 without"
                     + " MODIFY_PHONE_STATE  callingPackage=" + callingPackage);
             return;
         }
         if ((streamType == AudioManager.STREAM_ASSISTANT)
-            && (mContext.checkCallingOrSelfPermission(
-                    android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+                && (mContext.checkCallingOrSelfPermission(MODIFY_AUDIO_ROUTING)
                     != PackageManager.PERMISSION_GRANTED)) {
             Log.w(TAG, "Trying to call setStreamVolume() for STREAM_ASSISTANT without"
                     + " MODIFY_AUDIO_ROUTING  callingPackage=" + callingPackage);
@@ -4348,7 +4360,7 @@
                 canChangeMuteAndUpdateController);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_ULTRASOUND)
+    @android.annotation.EnforcePermission(Manifest.permission.ACCESS_ULTRASOUND)
     /** @see AudioManager#isUltrasoundSupported() */
     public boolean isUltrasoundSupported() {
         super.isUltrasoundSupported_enforcePermission();
@@ -4356,8 +4368,8 @@
         return AudioSystem.isUltrasoundSupported();
     }
 
-    /** @see AudioManager#isHotwordStreamSupported() */
-    @android.annotation.EnforcePermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD)
+    /** @see AudioManager#isHotwordStreamSupported(boolean)  */
+    @android.annotation.EnforcePermission(CAPTURE_AUDIO_HOTWORD)
     public boolean isHotwordStreamSupported(boolean lookbackAudio) {
         super.isHotwordStreamSupported_enforcePermission();
         try {
@@ -4373,7 +4385,7 @@
     private boolean canChangeAccessibilityVolume() {
         synchronized (mAccessibilityServiceUidsLock) {
             if (PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
-                    android.Manifest.permission.CHANGE_ACCESSIBILITY_VOLUME)) {
+                    Manifest.permission.CHANGE_ACCESSIBILITY_VOLUME)) {
                 return true;
             }
             if (mAccessibilityServiceUids != null) {
@@ -4550,15 +4562,20 @@
                 + featureSpatialAudioHeadtrackingLowLatency());
         pw.println("\tandroid.media.audio.focusFreezeTestApi:"
                 + focusFreezeTestApi());
+        pw.println("\tcom.android.media.audio.audioserverPermissions:"
+                + audioserverPermissions());
         pw.println("\tcom.android.media.audio.disablePrescaleAbsoluteVolume:"
                 + disablePrescaleAbsoluteVolume());
-
         pw.println("\tcom.android.media.audio.setStreamVolumeOrder:"
                 + setStreamVolumeOrder());
         pw.println("\tandroid.media.audio.roForegroundAudioControl:"
                 + roForegroundAudioControl());
+        pw.println("\tandroid.media.audio.scoManagedByAudio:"
+                + scoManagedByAudio());
         pw.println("\tcom.android.media.audio.vgsVssSyncMuteOrder:"
                 + vgsVssSyncMuteOrder());
+        pw.println("\tcom.android.media.audio.absVolumeIndexFix:"
+                + absVolumeIndexFix());
     }
 
     private void dumpAudioMode(PrintWriter pw) {
@@ -4754,7 +4771,9 @@
             }
         }
 
-        if (streamTypeAlias == AudioSystem.STREAM_MUSIC
+        int streamToDriveAbsVol = absVolumeIndexFix() ? getBluetoothContextualVolumeStream() :
+                AudioSystem.STREAM_MUSIC;
+        if (streamTypeAlias == streamToDriveAbsVol
                 && AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
                 && (flags & AudioManager.FLAG_BLUETOOTH_ABS_VOLUME) == 0) {
             if (DEBUG_VOL) {
@@ -4874,7 +4893,7 @@
 
     /** @see AudioManager#forceVolumeControlStream(int) */
     public void forceVolumeControlStream(int streamType, IBinder cb) {
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+        if (mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
                 != PackageManager.PERMISSION_GRANTED) {
             return;
         }
@@ -5127,7 +5146,7 @@
             return;
         }
         if ((PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
-                        android.Manifest.permission.CAPTURE_AUDIO_OUTPUT))) {
+                        CAPTURE_AUDIO_OUTPUT))) {
             Log.w(TAG, "Trying to call forceRemoteSubmixFullVolume() without CAPTURE_AUDIO_OUTPUT");
             return;
         }
@@ -5174,8 +5193,7 @@
             return;
         }
         if (userId != UserHandle.getCallingUserId() &&
-                mContext.checkPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
-                        pid, uid)
+                mContext.checkPermission(INTERACT_ACROSS_USERS_FULL, pid, uid)
                 != PackageManager.PERMISSION_GRANTED) {
             return;
         }
@@ -5216,7 +5234,7 @@
         return mMasterMute.get();
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     /** @see AudioManager#setMasterMute(boolean, int) */
     public void setMasterMute(boolean mute, int flags, String callingPackage, int userId,
             String attributionTag) {
@@ -5252,9 +5270,7 @@
 
     @Override
     @android.annotation.EnforcePermission(anyOf = {
-            android.Manifest.permission.MODIFY_AUDIO_ROUTING,
-            android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
-    })
+            MODIFY_AUDIO_ROUTING, MODIFY_AUDIO_SETTINGS_PRIVILEGED })
     /**
      * @see AudioDeviceVolumeManager#getDeviceVolume(VolumeInfo, AudioDeviceAttributes)
      */
@@ -5302,12 +5318,12 @@
         final boolean isPrivileged =
                 Binder.getCallingUid() == Process.SYSTEM_UID
                  || callingHasAudioSettingsPermission()
-                 || (mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
+                 || (mContext.checkCallingPermission(MODIFY_AUDIO_ROUTING)
                         == PackageManager.PERMISSION_GRANTED);
         return (mStreamStates[streamType].getMinIndex(isPrivileged) + 5) / 10;
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.QUERY_AUDIO_STATE)
+    @android.annotation.EnforcePermission(QUERY_AUDIO_STATE)
     /** Get last audible volume before stream was muted. */
     public int getLastAudibleStreamVolume(int streamType) {
         super.getLastAudibleStreamVolume_enforcePermission();
@@ -5469,8 +5485,7 @@
             return;
         }
         if (userId != UserHandle.getCallingUserId() &&
-                mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+                mContext.checkCallingOrSelfPermission(INTERACT_ACROSS_USERS_FULL)
                 != PackageManager.PERMISSION_GRANTED) {
             mmi.set(MediaMetrics.Property.EARLY_RETURN, "permission").record();
             return;
@@ -6060,7 +6075,7 @@
         }
 
         final boolean hasModifyPhoneStatePermission = mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.MODIFY_PHONE_STATE)
+                MODIFY_PHONE_STATE)
                 == PackageManager.PERMISSION_GRANTED;
         if ((mode == AudioSystem.MODE_IN_CALL
                 || mode == AudioSystem.MODE_CALL_REDIRECT
@@ -6207,6 +6222,17 @@
 
                 setLeAudioVolumeOnModeUpdate(mode, device, streamAlias, index, maxIndex);
 
+                synchronized (mCachedAbsVolDrivingStreamsLock) {
+                    mCachedAbsVolDrivingStreams.replaceAll((absDev, stream) -> {
+                        int streamToDriveAbs = getBluetoothContextualVolumeStream();
+                        if (stream != streamToDriveAbs) {
+                            mAudioSystem.setDeviceAbsoluteVolumeEnabled(absDev, /*address=*/
+                                    "", /*enabled*/true, streamToDriveAbs);
+                        }
+                        return streamToDriveAbs;
+                    });
+                }
+
                 // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all SCO
                 // connections not started by the application changing the mode when pid changes
                 mDeviceBroker.postSetModeOwner(mode, pid, uid);
@@ -6267,7 +6293,7 @@
         mModeDispatchers.unregister(dispatcher);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION)
+    @android.annotation.EnforcePermission(CALL_AUDIO_INTERCEPTION)
     /** @see AudioManager#isPstnCallAudioInterceptable() */
     public boolean isPstnCallAudioInterceptable() {
 
@@ -6293,7 +6319,7 @@
     @Override
     public void setRttEnabled(boolean rttEnabled) {
         if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.MODIFY_PHONE_STATE)
+                MODIFY_PHONE_STATE)
                 != PackageManager.PERMISSION_GRANTED) {
             Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setRttEnabled from pid="
                     + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
@@ -6585,8 +6611,7 @@
                             ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED)
                     .record();
         }
-        final boolean isPrivileged = mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.MODIFY_PHONE_STATE)
+        final boolean isPrivileged = mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
                 == PackageManager.PERMISSION_GRANTED;
         final long ident = Binder.clearCallingIdentity();
         try {
@@ -6636,8 +6661,7 @@
         if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
             return;
         }
-        final boolean isPrivileged = mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.MODIFY_PHONE_STATE)
+        final boolean isPrivileged = mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
                 == PackageManager.PERMISSION_GRANTED;
 
         // for logging only
@@ -6704,7 +6728,7 @@
     }
 
     /** @see AudioManager#setA2dpSuspended(boolean) */
-    @android.annotation.EnforcePermission(android.Manifest.permission.BLUETOOTH_STACK)
+    @android.annotation.EnforcePermission(BLUETOOTH_STACK)
     public void setA2dpSuspended(boolean enable) {
         super.setA2dpSuspended_enforcePermission();
         final String eventSource = new StringBuilder("setA2dpSuspended(").append(enable)
@@ -6714,7 +6738,7 @@
     }
 
     /** @see AudioManager#setA2dpSuspended(boolean) */
-    @android.annotation.EnforcePermission(android.Manifest.permission.BLUETOOTH_STACK)
+    @android.annotation.EnforcePermission(BLUETOOTH_STACK)
     public void setLeAudioSuspended(boolean enable) {
         super.setLeAudioSuspended_enforcePermission();
         final String eventSource = new StringBuilder("setLeAudioSuspended(").append(enable)
@@ -6819,8 +6843,7 @@
             mmi.set(MediaMetrics.Property.EARLY_RETURN, "permission or systemReady").record();
             return;
         }
-        final boolean isPrivileged = mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.MODIFY_PHONE_STATE)
+        final boolean isPrivileged = mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
                 == PackageManager.PERMISSION_GRANTED;
         final long ident = Binder.clearCallingIdentity();
         try {
@@ -6843,8 +6866,7 @@
         final String eventSource =  new StringBuilder("stopBluetoothSco()")
                 .append(") from u/pid:").append(uid).append("/")
                 .append(pid).toString();
-        final boolean isPrivileged = mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.MODIFY_PHONE_STATE)
+        final boolean isPrivileged = mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
                 == PackageManager.PERMISSION_GRANTED;
         final long ident = Binder.clearCallingIdentity();
         try {
@@ -7325,17 +7347,17 @@
     }
 
     private boolean callingOrSelfHasAudioSettingsPermission() {
-        return mContext.checkCallingOrSelfPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
+        return mContext.checkCallingOrSelfPermission(MODIFY_AUDIO_SETTINGS)
                 == PackageManager.PERMISSION_GRANTED;
     }
 
     private boolean callingHasAudioSettingsPermission() {
-        return mContext.checkCallingPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS)
+        return mContext.checkCallingPermission(MODIFY_AUDIO_SETTINGS)
                 == PackageManager.PERMISSION_GRANTED;
     }
 
     private boolean hasAudioSettingsPermission(int uid, int pid) {
-        return mContext.checkPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS, pid, uid)
+        return mContext.checkPermission(MODIFY_AUDIO_SETTINGS, pid, uid)
                 == PackageManager.PERMISSION_GRANTED;
     }
 
@@ -7527,17 +7549,16 @@
      * @param register Whether the listener is to be registered or unregistered. If false, the
      *                 device adopts variable volume behavior.
      */
-    @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED })
+    @RequiresPermission(anyOf = { MODIFY_AUDIO_ROUTING, BLUETOOTH_PRIVILEGED })
     public void registerDeviceVolumeDispatcherForAbsoluteVolume(boolean register,
             IAudioDeviceVolumeDispatcher cb, String packageName,
             AudioDeviceAttributes device, List<VolumeInfo> volumes,
             boolean handlesVolumeAdjustment,
             @AudioManager.AbsoluteDeviceVolumeBehavior int deviceVolumeBehavior) {
         // verify permissions
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+        if (mContext.checkCallingOrSelfPermission(MODIFY_AUDIO_ROUTING)
                 != PackageManager.PERMISSION_GRANTED
-                && mContext.checkCallingOrSelfPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+                && mContext.checkCallingOrSelfPermission(BLUETOOTH_PRIVILEGED)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException(
                     "Missing MODIFY_AUDIO_ROUTING or BLUETOOTH_PRIVILEGED permissions");
@@ -7595,9 +7616,7 @@
      * @param deviceVolumeBehavior one of the device behaviors
      */
     @android.annotation.EnforcePermission(anyOf = {
-            android.Manifest.permission.MODIFY_AUDIO_ROUTING,
-            android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
-    })
+            MODIFY_AUDIO_ROUTING, MODIFY_AUDIO_SETTINGS_PRIVILEGED })
     public void setDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device,
             @AudioManager.DeviceVolumeBehavior int deviceVolumeBehavior, @Nullable String pkgName) {
         // verify permissions
@@ -7680,9 +7699,7 @@
      * @return the volume behavior for the device
      */
     @android.annotation.EnforcePermission(anyOf = {
-            android.Manifest.permission.MODIFY_AUDIO_ROUTING,
-            android.Manifest.permission.QUERY_AUDIO_STATE,
-            android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED
+            MODIFY_AUDIO_ROUTING, QUERY_AUDIO_STATE,  MODIFY_AUDIO_SETTINGS_PRIVILEGED
     })
     public @AudioManager.DeviceVolumeBehavior
     int getDeviceVolumeBehavior(@NonNull AudioDeviceAttributes device) {
@@ -7770,7 +7787,7 @@
      */
     private static final byte[] DEFAULT_ARC_AUDIO_DESCRIPTOR = new byte[]{0x09, 0x7f, 0x07};
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     /**
      * see AudioManager.setWiredDeviceConnectionState()
      */
@@ -7880,7 +7897,7 @@
     public @interface BtProfile {}
 
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.BLUETOOTH_STACK)
+    @android.annotation.EnforcePermission(BLUETOOTH_STACK)
     /**
      * See AudioManager.handleBluetoothActiveDeviceChanged(...)
      */
@@ -7895,7 +7912,8 @@
         if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK
                 && profile != BluetoothProfile.LE_AUDIO
                 && profile != BluetoothProfile.LE_AUDIO_BROADCAST
-                && profile != BluetoothProfile.HEARING_AID) {
+                && profile != BluetoothProfile.HEARING_AID
+                && !(mDeviceBroker.isScoManagedByAudio() && profile == BluetoothProfile.HEADSET)) {
             throw new IllegalArgumentException("Illegal BluetoothProfile profile for device "
                     + previousDevice + " -> " + newDevice + ". Got: " + profile);
         }
@@ -8136,6 +8154,10 @@
             return mAudioVolumeGroup.name();
         }
 
+        public int getId() {
+            return mAudioVolumeGroup.getId();
+        }
+
         /**
          * Volume group with non null minimum index are considered as non mutable, thus
          * bijectivity is broken with potential associated stream type.
@@ -8786,24 +8808,30 @@
         }
 
         private int getAbsoluteVolumeIndex(int index) {
-            /* Special handling for Bluetooth Absolute Volume scenario
-             * If we send full audio gain, some accessories are too loud even at its lowest
-             * volume. We are not able to enumerate all such accessories, so here is the
-             * workaround from phone side.
-             * Pre-scale volume at lowest volume steps 1 2 and 3.
-             * For volume step 0, set audio gain to 0 as some accessories won't mute on their end.
-             */
-            if (index == 0) {
-                // 0% for volume 0
-                index = 0;
-            } else if (!disablePrescaleAbsoluteVolume() && index > 0 && index <= 3) {
-                // Pre-scale for volume steps 1 2 and 3
-                index = (int) (mIndexMax * mPrescaleAbsoluteVolume[index - 1]) / 10;
+            if (absVolumeIndexFix()) {
+                // The attenuation is applied in the APM. No need to manipulate the index here
+                return index;
             } else {
-                // otherwise, full gain
-                index = (mIndexMax + 5) / 10;
+                /* Special handling for Bluetooth Absolute Volume scenario
+                 * If we send full audio gain, some accessories are too loud even at its lowest
+                 * volume. We are not able to enumerate all such accessories, so here is the
+                 * workaround from phone side.
+                 * Pre-scale volume at lowest volume steps 1 2 and 3.
+                 * For volume step 0, set audio gain to 0 as some accessories won't mute on their
+                 * end.
+                 */
+                if (index == 0) {
+                    // 0% for volume 0
+                    index = 0;
+                } else if (!disablePrescaleAbsoluteVolume() && index > 0 && index <= 3) {
+                    // Pre-scale for volume steps 1 2 and 3
+                    index = (int) (mIndexMax * mPrescaleAbsoluteVolume[index - 1]) / 10;
+                } else {
+                    // otherwise, full gain
+                    index = (mIndexMax + 5) / 10;
+                }
+                return index;
             }
-            return index;
         }
 
         private void setStreamVolumeIndex(int index, int device) {
@@ -8814,6 +8842,11 @@
                     && !isFullyMuted()) {
                 index = 1;
             }
+
+            if (DEBUG_VOL) {
+                Log.d(TAG, "setStreamVolumeIndexAS(" + mStreamType + ", " + index + ", " + device
+                        + ")");
+            }
             mAudioSystem.setStreamVolumeIndexAS(mStreamType, index, device);
         }
 
@@ -8825,14 +8858,24 @@
             } else if (isAbsoluteVolumeDevice(device)
                     || isA2dpAbsoluteVolumeDevice(device)
                     || AudioSystem.isLeAudioDeviceType(device)) {
-                index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
+                // do not change the volume logic for dynamic abs behavior devices like HDMI
+                if (absVolumeIndexFix() && isAbsoluteVolumeDevice(device)) {
+                    index = getAbsoluteVolumeIndex((mIndexMax + 5) / 10);
+                } else {
+                    index = getAbsoluteVolumeIndex((getIndex(device) + 5) / 10);
+                }
             } else if (isFullVolumeDevice(device)) {
                 index = (mIndexMax + 5)/10;
             } else if (device == AudioSystem.DEVICE_OUT_HEARING_AID) {
-                index = (mIndexMax + 5)/10;
+                if (absVolumeIndexFix()) {
+                    index = getAbsoluteVolumeIndex((getIndex(device) + 5) / 10);
+                } else {
+                    index = (mIndexMax + 5) / 10;
+                }
             } else {
                 index = (getIndex(device) + 5)/10;
             }
+
             setStreamVolumeIndex(index, device);
         }
 
@@ -8850,11 +8893,22 @@
                                 || isA2dpAbsoluteVolumeDevice(device)
                                 || AudioSystem.isLeAudioDeviceType(device)) {
                             isAbsoluteVolume = true;
-                            index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10);
+                            // do not change the volume logic for dynamic abs behavior devices
+                            // like HDMI
+                            if (absVolumeIndexFix() && isAbsoluteVolumeDevice(device)) {
+                                index = getAbsoluteVolumeIndex((mIndexMax + 5) / 10);
+                            } else {
+                                index = getAbsoluteVolumeIndex((getIndex(device) + 5) / 10);
+                            }
                         } else if (isFullVolumeDevice(device)) {
                             index = (mIndexMax + 5)/10;
                         } else if (device == AudioSystem.DEVICE_OUT_HEARING_AID) {
-                            index = (mIndexMax + 5)/10;
+                            if (absVolumeIndexFix()) {
+                                isAbsoluteVolume = true;
+                                index = getAbsoluteVolumeIndex((getIndex(device) + 5) / 10);
+                            } else {
+                                index = (mIndexMax + 5) / 10;
+                            }
                         } else {
                             index = (mIndexMap.valueAt(i) + 5)/10;
                         }
@@ -9851,6 +9905,27 @@
 
     /*package*/ void setAvrcpAbsoluteVolumeSupported(boolean support) {
         mAvrcpAbsVolSupported = support;
+        if (absVolumeIndexFix()) {
+            int a2dpDev = AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP;
+            synchronized (mCachedAbsVolDrivingStreamsLock) {
+                mCachedAbsVolDrivingStreams.compute(a2dpDev, (dev, stream) -> {
+                    if (stream != null && !mAvrcpAbsVolSupported) {
+                        mAudioSystem.setDeviceAbsoluteVolumeEnabled(a2dpDev, /*address=*/
+                                "", /*enabled*/false, AudioSystem.DEVICE_NONE);
+                        return null;
+                    }
+                    // For A2DP and AVRCP we need to set the driving stream based on the
+                    // BT contextual stream. Hence, we need to make sure in adjustStreamVolume
+                    // and setStreamVolume that the driving abs volume stream is consistent.
+                    int streamToDriveAbs = getBluetoothContextualVolumeStream();
+                    if (stream == null || stream != streamToDriveAbs) {
+                        mAudioSystem.setDeviceAbsoluteVolumeEnabled(a2dpDev, /*address=*/
+                                "", /*enabled*/true, streamToDriveAbs);
+                    }
+                    return streamToDriveAbs;
+                });
+            }
+        }
         sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, SENDMSG_QUEUE,
                     AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 0,
                     mStreamStates[AudioSystem.STREAM_MUSIC], 0);
@@ -10156,8 +10231,8 @@
         if (AudioAttributes.isSystemUsage(usage)) {
             if ((usage == AudioAttributes.USAGE_CALL_ASSISTANT
                     && (audioAttributes.getAllFlags() & AudioAttributes.FLAG_CALL_REDIRECTION) != 0
-                    && callerHasPermission(Manifest.permission.CALL_AUDIO_INTERCEPTION))
-                    || callerHasPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)) {
+                    && callerHasPermission(CALL_AUDIO_INTERCEPTION))
+                    || callerHasPermission(MODIFY_AUDIO_ROUTING)) {
                 if (!isSupportedSystemUsage(usage)) {
                     throw new IllegalArgumentException(
                             "Unsupported usage " + AudioAttributes.usageToString(usage));
@@ -10175,8 +10250,8 @@
                     && ((usage == AudioAttributes.USAGE_CALL_ASSISTANT
                         && (audioAttributes.getAllFlags()
                             & AudioAttributes.FLAG_CALL_REDIRECTION) != 0
-                        && callerHasPermission(Manifest.permission.CALL_AUDIO_INTERCEPTION))
-                        || callerHasPermission(Manifest.permission.MODIFY_AUDIO_ROUTING));
+                        && callerHasPermission(CALL_AUDIO_INTERCEPTION))
+                        || callerHasPermission(MODIFY_AUDIO_ROUTING));
         }
         return true;
     }
@@ -10207,7 +10282,7 @@
         if ((flags & AudioManager.AUDIOFOCUS_FLAG_LOCK) == AudioManager.AUDIOFOCUS_FLAG_LOCK) {
             if (AudioSystem.IN_VOICE_COMM_FOCUS_ID.equals(clientId)) {
                 if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
-                            android.Manifest.permission.MODIFY_PHONE_STATE)) {
+                            MODIFY_PHONE_STATE)) {
                     final String reason = "Invalid permission to (un)lock audio focus";
                     Log.e(TAG, reason, new Exception());
                     mmi.set(MediaMetrics.Property.EARLY_RETURN, reason)
@@ -10239,10 +10314,9 @@
 
         // does caller have system privileges to bypass HardeningEnforcer
         boolean permissionOverridesCheck = false;
-        if ((mContext.checkCallingOrSelfPermission(
-                Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+        if ((mContext.checkCallingOrSelfPermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
                 == PackageManager.PERMISSION_GRANTED)
-                || (mContext.checkCallingOrSelfPermission(Manifest.permission.MODIFY_AUDIO_ROUTING)
+                || (mContext.checkCallingOrSelfPermission(MODIFY_AUDIO_ROUTING)
                 == PackageManager.PERMISSION_GRANTED)) {
             permissionOverridesCheck = true;
         } else if (uid < UserHandle.AID_APP_START) {
@@ -10376,7 +10450,7 @@
      *     such as another freeze currently used.
      */
     @Override
-    @EnforcePermission("android.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+    @EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
     public boolean enterAudioFocusFreezeForTest(IBinder cb, int[] exemptedUids) {
         super.enterAudioFocusFreezeForTest_enforcePermission();
         Objects.requireNonNull(exemptedUids);
@@ -10392,7 +10466,7 @@
      *     such as the freeze already having ended, or not started.
      */
     @Override
-    @EnforcePermission("android.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+    @EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
     public boolean exitAudioFocusFreezeForTest(IBinder cb) {
         super.exitAudioFocusFreezeForTest_enforcePermission();
         Objects.requireNonNull(cb);
@@ -10438,8 +10512,7 @@
     private static final boolean SPATIAL_AUDIO_ENABLED_DEFAULT = true;
 
     private void enforceModifyDefaultAudioEffectsPermission() {
-        if (mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+        if (mContext.checkCallingOrSelfPermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Missing MODIFY_DEFAULT_AUDIO_EFFECTS permission");
         }
@@ -10463,7 +10536,7 @@
         return mSpatializerHelper.isAvailable();
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#isAvailableForDevice(AudioDeviceAttributes) */
     public boolean isSpatializerAvailableForDevice(@NonNull AudioDeviceAttributes device)  {
         super.isSpatializerAvailableForDevice_enforcePermission();
@@ -10471,7 +10544,7 @@
         return mSpatializerHelper.isAvailableForDevice(Objects.requireNonNull(device));
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#hasHeadTracker(AudioDeviceAttributes) */
     public boolean hasHeadTracker(@NonNull AudioDeviceAttributes device) {
         super.hasHeadTracker_enforcePermission();
@@ -10479,7 +10552,7 @@
         return mSpatializerHelper.hasHeadTracker(Objects.requireNonNull(device));
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#setHeadTrackerEnabled(boolean, AudioDeviceAttributes) */
     public void setHeadTrackerEnabled(boolean enabled, @NonNull AudioDeviceAttributes device) {
         super.setHeadTrackerEnabled_enforcePermission();
@@ -10487,7 +10560,7 @@
         mSpatializerHelper.setHeadTrackerEnabled(enabled, Objects.requireNonNull(device));
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#isHeadTrackerEnabled(AudioDeviceAttributes) */
     public boolean isHeadTrackerEnabled(@NonNull AudioDeviceAttributes device) {
         super.isHeadTrackerEnabled_enforcePermission();
@@ -10500,7 +10573,7 @@
         return mSpatializerHelper.isHeadTrackerAvailable();
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#setSpatializerEnabled(boolean) */
     public void setSpatializerEnabled(boolean enabled) {
         super.setSpatializerEnabled_enforcePermission();
@@ -10530,7 +10603,7 @@
         mSpatializerHelper.unregisterStateCallback(cb);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#SpatializerHeadTrackingDispatcherStub */
     public void registerSpatializerHeadTrackingCallback(
             @NonNull ISpatializerHeadTrackingModeCallback cb) {
@@ -10540,7 +10613,7 @@
         mSpatializerHelper.registerHeadTrackingModeCallback(cb);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#SpatializerHeadTrackingDispatcherStub */
     public void unregisterSpatializerHeadTrackingCallback(
             @NonNull ISpatializerHeadTrackingModeCallback cb) {
@@ -10557,7 +10630,7 @@
         mSpatializerHelper.registerHeadTrackerAvailableCallback(cb, register);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#setOnHeadToSoundstagePoseUpdatedListener */
     public void registerHeadToSoundstagePoseCallback(
             @NonNull ISpatializerHeadToSoundStagePoseCallback cb) {
@@ -10567,7 +10640,7 @@
         mSpatializerHelper.registerHeadToSoundstagePoseCallback(cb);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#clearOnHeadToSoundstagePoseUpdatedListener */
     public void unregisterHeadToSoundstagePoseCallback(
             @NonNull ISpatializerHeadToSoundStagePoseCallback cb) {
@@ -10577,7 +10650,7 @@
         mSpatializerHelper.unregisterHeadToSoundstagePoseCallback(cb);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#getSpatializerCompatibleAudioDevices() */
     public @NonNull List<AudioDeviceAttributes> getSpatializerCompatibleAudioDevices() {
         super.getSpatializerCompatibleAudioDevices_enforcePermission();
@@ -10585,7 +10658,7 @@
         return mSpatializerHelper.getCompatibleAudioDevices();
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#addSpatializerCompatibleAudioDevice(AudioDeviceAttributes) */
     public void addSpatializerCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
         super.addSpatializerCompatibleAudioDevice_enforcePermission();
@@ -10594,7 +10667,7 @@
         mSpatializerHelper.addCompatibleAudioDevice(ada);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#removeSpatializerCompatibleAudioDevice(AudioDeviceAttributes) */
     public void removeSpatializerCompatibleAudioDevice(@NonNull AudioDeviceAttributes ada) {
         super.removeSpatializerCompatibleAudioDevice_enforcePermission();
@@ -10603,7 +10676,7 @@
         mSpatializerHelper.removeCompatibleAudioDevice(ada);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#getSupportedHeadTrackingModes() */
     public int[] getSupportedHeadTrackingModes() {
         super.getSupportedHeadTrackingModes_enforcePermission();
@@ -10611,7 +10684,7 @@
         return mSpatializerHelper.getSupportedHeadTrackingModes();
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#getHeadTrackingMode() */
     public int getActualHeadTrackingMode() {
         super.getActualHeadTrackingMode_enforcePermission();
@@ -10619,7 +10692,7 @@
         return mSpatializerHelper.getActualHeadTrackingMode();
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#getDesiredHeadTrackingMode() */
     public int getDesiredHeadTrackingMode() {
         super.getDesiredHeadTrackingMode_enforcePermission();
@@ -10627,7 +10700,7 @@
         return mSpatializerHelper.getDesiredHeadTrackingMode();
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#setGlobalTransform */
     public void setSpatializerGlobalTransform(@NonNull float[] transform) {
         super.setSpatializerGlobalTransform_enforcePermission();
@@ -10636,7 +10709,7 @@
         mSpatializerHelper.setGlobalTransform(transform);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#recenterHeadTracker() */
     public void recenterHeadTracker() {
         super.recenterHeadTracker_enforcePermission();
@@ -10644,7 +10717,7 @@
         mSpatializerHelper.recenterHeadTracker();
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#setDesiredHeadTrackingMode */
     public void setDesiredHeadTrackingMode(@Spatializer.HeadTrackingModeSet int mode) {
         super.setDesiredHeadTrackingMode_enforcePermission();
@@ -10660,7 +10733,7 @@
         mSpatializerHelper.setDesiredHeadTrackingMode(mode);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#setEffectParameter */
     public void setSpatializerParameter(int key, @NonNull byte[] value) {
         super.setSpatializerParameter_enforcePermission();
@@ -10669,7 +10742,7 @@
         mSpatializerHelper.setEffectParameter(key, value);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#getEffectParameter */
     public void getSpatializerParameter(int key, @NonNull byte[] value) {
         super.getSpatializerParameter_enforcePermission();
@@ -10678,7 +10751,7 @@
         mSpatializerHelper.getEffectParameter(key, value);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#getOutput */
     public int getSpatializerOutput() {
         super.getSpatializerOutput_enforcePermission();
@@ -10686,7 +10759,7 @@
         return mSpatializerHelper.getOutput();
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#setOnSpatializerOutputChangedListener */
     public void registerSpatializerOutputCallback(ISpatializerOutputCallback cb) {
         super.registerSpatializerOutputCallback_enforcePermission();
@@ -10695,7 +10768,7 @@
         mSpatializerHelper.registerSpatializerOutputCallback(cb);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_DEFAULT_AUDIO_EFFECTS)
+    @android.annotation.EnforcePermission(MODIFY_DEFAULT_AUDIO_EFFECTS)
     /** @see Spatializer#clearOnSpatializerOutputChangedListener */
     public void unregisterSpatializerOutputCallback(ISpatializerOutputCallback cb) {
         super.unregisterSpatializerOutputCallback_enforcePermission();
@@ -10742,7 +10815,7 @@
 
     private boolean isBluetoothPrividged() {
         return PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
-                android.Manifest.permission.BLUETOOTH_CONNECT)
+                Manifest.permission.BLUETOOTH_CONNECT)
                 || Binder.getCallingUid() == Process.SYSTEM_UID;
     }
 
@@ -10949,7 +11022,7 @@
         });
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     /** @see AudioManager#getMutingExpectedDevice */
     public @Nullable AudioDeviceAttributes getMutingExpectedDevice() {
         super.getMutingExpectedDevice_enforcePermission();
@@ -11000,7 +11073,7 @@
     final RemoteCallbackList<IMuteAwaitConnectionCallback> mMuteAwaitConnectionDispatchers =
             new RemoteCallbackList<IMuteAwaitConnectionCallback>();
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     /** @see AudioManager#registerMuteAwaitConnectionCallback */
     public void registerMuteAwaitConnectionDispatcher(@NonNull IMuteAwaitConnectionCallback cb,
             boolean register) {
@@ -11175,7 +11248,7 @@
         }
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.REMOTE_AUDIO_PLAYBACK)
+    @android.annotation.EnforcePermission(Manifest.permission.REMOTE_AUDIO_PLAYBACK)
     @Override
     public void setRingtonePlayer(IRingtonePlayer player) {
         setRingtonePlayer_enforcePermission();
@@ -11376,7 +11449,6 @@
 
     @Override
     @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
-    @AudioDeviceCategory
     public boolean isBluetoothAudioDeviceCategoryFixed(@NonNull String address) {
         super.isBluetoothAudioDeviceCategoryFixed_enforcePermission();
         if (!automaticBtDeviceType()) {
@@ -11870,6 +11942,45 @@
     private static final String mMetricsId = MediaMetrics.Name.AUDIO_SERVICE
             + MediaMetrics.SEPARATOR;
 
+    private static AudioServerPermissionProvider initializeAudioServerPermissionProvider(
+            Context context, AudioPolicyFacade audioPolicy, Executor audioserverExecutor) {
+        Collection<PackageState> packageStates = null;
+        try (PackageManagerLocal.UnfilteredSnapshot snapshot =
+                    LocalManagerRegistry.getManager(PackageManagerLocal.class)
+                        .withUnfilteredSnapshot()) {
+            packageStates = snapshot.getPackageStates().values();
+        }
+        var provider = new AudioServerPermissionProvider(packageStates);
+        audioPolicy.registerOnStartTask(() -> {
+            provider.onServiceStart(audioPolicy.getPermissionController());
+        });
+
+        // Set up event listeners
+        IntentFilter packageUpdateFilter = new IntentFilter();
+        packageUpdateFilter.addAction(ACTION_PACKAGE_ADDED);
+        packageUpdateFilter.addAction(ACTION_PACKAGE_REMOVED);
+        packageUpdateFilter.addDataScheme("package");
+
+        context.registerReceiverForAllUsers(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                String action = intent.getAction();
+                String pkgName = intent.getData().getEncodedSchemeSpecificPart();
+                int uid = intent.getIntExtra(Intent.EXTRA_UID, Process.INVALID_UID);
+                if (intent.getBooleanExtra(EXTRA_REPLACING, false) ||
+                        intent.getBooleanExtra(EXTRA_ARCHIVAL, false)) return;
+                if (action.equals(ACTION_PACKAGE_ADDED)) {
+                    audioserverExecutor.execute(() ->
+                            provider.onModifyPackageState(uid, pkgName, false /* isRemoved */));
+                } else if (action.equals(ACTION_PACKAGE_REMOVED)) {
+                    audioserverExecutor.execute(() ->
+                            provider.onModifyPackageState(uid, pkgName, true /* isRemoved */));
+                }
+            }
+        }, packageUpdateFilter, null, null); // main thread is fine, since dispatch on executor
+        return provider;
+    }
+
     // Inform AudioFlinger of our device's low RAM attribute
     private static void readAndSetLowRamDevice()
     {
@@ -11892,7 +12003,7 @@
     }
 
     private void enforceVolumeController(String action) {
-        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.STATUS_BAR_SERVICE,
                 "Only SystemUI can " + action);
     }
 
@@ -12396,8 +12507,8 @@
         }
 
         if (requireCaptureAudioOrMediaOutputPerm
-                && !callerHasPermission(android.Manifest.permission.CAPTURE_MEDIA_OUTPUT)
-                && !callerHasPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT)) {
+                && !callerHasPermission(CAPTURE_MEDIA_OUTPUT)
+                && !callerHasPermission(CAPTURE_AUDIO_OUTPUT)) {
             Log.e(TAG, "Privileged audio capture requires CAPTURE_MEDIA_OUTPUT or "
                       + "CAPTURE_AUDIO_OUTPUT system permission");
             return false;
@@ -12405,7 +12516,7 @@
 
         if (voiceCommunicationCaptureMixes != null && voiceCommunicationCaptureMixes.size() > 0) {
             if (!callerHasPermission(
-                    android.Manifest.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT)) {
+                    Manifest.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT)) {
                 Log.e(TAG, "Audio capture for voice communication requires "
                         + "CAPTURE_VOICE_COMMUNICATION_OUTPUT system permission");
                 return false;
@@ -12422,13 +12533,12 @@
         }
 
         if (requireModifyRouting
-                && !callerHasPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)) {
+                && !callerHasPermission(MODIFY_AUDIO_ROUTING)) {
             Log.e(TAG, "Can not capture audio without MODIFY_AUDIO_ROUTING");
             return false;
         }
 
-        if (requireCallAudioInterception
-                && !callerHasPermission(android.Manifest.permission.CALL_AUDIO_INTERCEPTION)) {
+        if (requireCallAudioInterception && !callerHasPermission(CALL_AUDIO_INTERCEPTION)) {
             Log.e(TAG, "Can not capture audio without CALL_AUDIO_INTERCEPTION");
             return false;
         }
@@ -12541,7 +12651,7 @@
         // permission check
         final boolean hasPermissionForPolicy =
                 (PackageManager.PERMISSION_GRANTED == mContext.checkCallingPermission(
-                        android.Manifest.permission.MODIFY_AUDIO_ROUTING));
+                        MODIFY_AUDIO_ROUTING));
         if (!hasPermissionForPolicy) {
             Slog.w(TAG, errorMsg + " for pid " +
                     + Binder.getCallingPid() + " / uid "
@@ -12625,7 +12735,7 @@
      * @return {@link AudioManager#SUCCESS} iff the mixing rules were updated successfully,
      *     {@link AudioManager#ERROR} otherwise.
      */
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     public int updateMixingRulesForPolicy(
             @NonNull AudioMix[] mixesToUpdate,
             @NonNull AudioMixingRule[] updatedMixingRules,
@@ -12753,7 +12863,7 @@
         return AudioManager.SUCCESS;
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     /** @see AudioPolicy#getFocusStack() */
     public List<AudioFocusInfo> getFocusStack() {
         super.getFocusStack_enforcePermission();
@@ -12779,8 +12889,7 @@
     /**
      * see {@link AudioPolicy#setFadeManagerConfigurationForFocusLoss(FadeManagerConfiguration)}
      */
-    @android.annotation.EnforcePermission(
-            android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
     public int setFadeManagerConfigurationForFocusLoss(
             @NonNull FadeManagerConfiguration fmcForFocusLoss) {
         super.setFadeManagerConfigurationForFocusLoss_enforcePermission();
@@ -12796,8 +12905,7 @@
     /**
      * see {@link AudioPolicy#clearFadeManagerConfigurationForFocusLoss()}
      */
-    @android.annotation.EnforcePermission(
-            android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
     public int clearFadeManagerConfigurationForFocusLoss() {
         super.clearFadeManagerConfigurationForFocusLoss_enforcePermission();
         ensureFadeManagerConfigIsEnabled();
@@ -12808,8 +12916,7 @@
     /**
      * see {@link AudioPolicy#getFadeManagerConfigurationForFocusLoss()}
      */
-    @android.annotation.EnforcePermission(
-            android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
     public FadeManagerConfiguration getFadeManagerConfigurationForFocusLoss() {
         super.getFadeManagerConfigurationForFocusLoss_enforcePermission();
         ensureFadeManagerConfigIsEnabled();
@@ -12970,7 +13077,7 @@
 
 
     /** @see AudioManager#supportsBluetoothVariableLatency() */
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     public boolean supportsBluetoothVariableLatency() {
         super.supportsBluetoothVariableLatency_enforcePermission();
         try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
@@ -12979,7 +13086,7 @@
     }
 
     /** @see AudioManager#setBluetoothVariableLatencyEnabled(boolean) */
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     public void setBluetoothVariableLatencyEnabled(boolean enabled) {
         super.setBluetoothVariableLatencyEnabled_enforcePermission();
         try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
@@ -12988,7 +13095,7 @@
     }
 
     /** @see AudioManager#isBluetoothVariableLatencyEnabled(boolean) */
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     public boolean isBluetoothVariableLatencyEnabled() {
         super.isBluetoothVariableLatencyEnabled_enforcePermission();
         try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
@@ -13075,7 +13182,7 @@
     public void registerRecordingCallback(IRecordingConfigDispatcher rcdb) {
         final boolean isPrivileged =
                 (PackageManager.PERMISSION_GRANTED == mContext.checkCallingPermission(
-                        android.Manifest.permission.MODIFY_AUDIO_ROUTING));
+                        MODIFY_AUDIO_ROUTING));
         mRecordMonitor.registerRecordingCallback(rcdb, isPrivileged);
     }
 
@@ -13086,7 +13193,7 @@
     public List<AudioRecordingConfiguration> getActiveRecordingConfigurations() {
         final boolean isPrivileged = Binder.getCallingUid() == Process.SYSTEM_UID
                 || (PackageManager.PERMISSION_GRANTED == mContext.checkCallingPermission(
-                        android.Manifest.permission.MODIFY_AUDIO_ROUTING));
+                        MODIFY_AUDIO_ROUTING));
         return mRecordMonitor.getActiveRecordingConfigurations(isPrivileged);
     }
 
@@ -13122,7 +13229,7 @@
     public void registerPlaybackCallback(IPlaybackConfigDispatcher pcdb) {
         final boolean isPrivileged =
                 (PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
-                        android.Manifest.permission.MODIFY_AUDIO_ROUTING));
+                        MODIFY_AUDIO_ROUTING));
         mPlaybackMonitor.registerPlaybackCallback(pcdb, isPrivileged);
     }
 
@@ -13133,7 +13240,7 @@
     public List<AudioPlaybackConfiguration> getActivePlaybackConfigurations() {
         final boolean isPrivileged =
                 (PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
-                        android.Manifest.permission.MODIFY_AUDIO_ROUTING));
+                        MODIFY_AUDIO_ROUTING));
         return mPlaybackMonitor.getActivePlaybackConfigurations(isPrivileged);
     }
 
@@ -13724,8 +13831,7 @@
      * see {@link AudioManager#dispatchAudioFocusChangeWithFade(AudioFocusInfo, int, AudioPolicy,
      * List, FadeManagerConfiguration)}
      */
-    @android.annotation.EnforcePermission(
-            android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
     public int dispatchFocusChangeWithFade(AudioFocusInfo afi, int focusChange,
             IAudioPolicyCallback pcb, List<AudioFocusInfo> otherActiveAfis,
             FadeManagerConfiguration transientFadeMgrConfig) {
@@ -13762,8 +13868,7 @@
     /**
      * @see AudioManager#shouldNotificationSoundPlay(AudioAttributes)
      */
-    @android.annotation.EnforcePermission(
-            android.Manifest.permission.QUERY_AUDIO_STATE)
+    @android.annotation.EnforcePermission(QUERY_AUDIO_STATE)
     public boolean shouldNotificationSoundPlay(@NonNull final AudioAttributes aa) {
         super.shouldNotificationSoundPlay_enforcePermission();
         Objects.requireNonNull(aa);
@@ -13823,12 +13928,10 @@
             new HashMap<IBinder, AsdProxy>();
 
     private void checkMonitorAudioServerStatePermission() {
-        if (!(mContext.checkCallingOrSelfPermission(
-                    android.Manifest.permission.MODIFY_PHONE_STATE) ==
-                PackageManager.PERMISSION_GRANTED ||
-              mContext.checkCallingOrSelfPermission(
-                    android.Manifest.permission.MODIFY_AUDIO_ROUTING) ==
-                PackageManager.PERMISSION_GRANTED)) {
+        if (!(mContext.checkCallingOrSelfPermission(MODIFY_PHONE_STATE)
+                == PackageManager.PERMISSION_GRANTED
+                || mContext.checkCallingOrSelfPermission(MODIFY_AUDIO_ROUTING)
+                == PackageManager.PERMISSION_GRANTED)) {
             throw new SecurityException("Not allowed to monitor audioserver state");
         }
     }
@@ -13923,7 +14026,7 @@
         AudioSystem.setAudioHalPids(pidsArray);
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     //======================
     // Multi Audio Focus
     //======================
@@ -13964,7 +14067,7 @@
      *     or the delay is not in range of {@link #getMaxAdditionalOutputDeviceDelay()}.
      */
     @Override
-    //@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    //@RequiresPermission(MODIFY_AUDIO_ROUTING)
     public boolean setAdditionalOutputDeviceDelay(
             @NonNull AudioDeviceAttributes device, @IntRange(from = 0) long delayMillis) {
         Objects.requireNonNull(device, "device must not be null");
@@ -14037,7 +14140,7 @@
         return delayMillis;
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     /** @see AudioManager#addAssistantServicesUids(int []) */
     @Override
     public void addAssistantServicesUids(int [] assistantUids) {
@@ -14050,7 +14153,7 @@
         }
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     /** @see AudioManager#removeAssistantServicesUids(int []) */
     @Override
     public void removeAssistantServicesUids(int [] assistantUids) {
@@ -14062,7 +14165,7 @@
         }
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     /** @see AudioManager#getAssistantServicesUids() */
     @Override
     public int[] getAssistantServicesUids() {
@@ -14075,7 +14178,7 @@
         return assistantUids;
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     /** @see AudioManager#setActiveAssistantServiceUids(int []) */
     @Override
     public void setActiveAssistantServiceUids(int [] activeAssistantUids) {
@@ -14088,7 +14191,7 @@
         updateActiveAssistantServiceUids();
     }
 
-    @android.annotation.EnforcePermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+    @android.annotation.EnforcePermission(MODIFY_AUDIO_ROUTING)
     /** @see AudioManager#getActiveAssistantServiceUids() */
     @Override
     public int[] getActiveAssistantServiceUids() {
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index 7202fa2..7f4bc74 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -598,6 +598,21 @@
     }
 
     /**
+     * Same as {@link AudioSystem#setDeviceAbsoluteVolumeEnabled(int, String, boolean, int)}
+     * @param nativeDeviceType the internal device type for which absolute volume is
+     *                         enabled/disabled
+     * @param address the address of the device for which absolute volume is enabled/disabled
+     * @param enabled whether the absolute volume is enabled/disabled
+     * @param streamToDriveAbs the stream that is controlling the absolute volume
+     * @return status of indicating the success of this operation
+     */
+    public int setDeviceAbsoluteVolumeEnabled(int nativeDeviceType, @NonNull String address,
+            boolean enabled, int streamToDriveAbs) {
+        return AudioSystem.setDeviceAbsoluteVolumeEnabled(nativeDeviceType, address, enabled,
+                streamToDriveAbs);
+    }
+
+    /**
      * Same as {@link AudioSystem#registerPolicyMixes(ArrayList, boolean)}
      * @param mixes
      * @param register
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 07daecd..991f94b 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -15,11 +15,7 @@
  */
 package com.android.server.audio;
 
-import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_CARKIT;
 import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_DEFAULT;
-import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_HEADSET;
-import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_HEARING_AID;
-import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_SPEAKER;
 import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET;
 import static android.bluetooth.BluetoothDevice.DEVICE_TYPE_WATCH;
 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_CARKIT;
@@ -94,14 +90,14 @@
     private final Map<BluetoothDevice, AudioDeviceAttributes> mResolvedScoAudioDevices =
             new HashMap<>();
 
-    private @Nullable BluetoothHearingAid mHearingAid;
+    private @Nullable BluetoothHearingAid mHearingAid = null;
 
-    private @Nullable BluetoothLeAudio mLeAudio;
+    private @Nullable BluetoothLeAudio mLeAudio = null;
 
     private @Nullable BluetoothLeAudioCodecConfig mLeAudioCodecConfig;
 
     // Reference to BluetoothA2dp to query for AbsoluteVolume.
-    private @Nullable BluetoothA2dp mA2dp;
+    private @Nullable BluetoothA2dp mA2dp = null;
 
     private @Nullable BluetoothCodecConfig mA2dpCodecConfig;
 
@@ -149,6 +145,14 @@
     private static final int BT_LE_AUDIO_MIN_VOL = 0;
     private static final int BT_LE_AUDIO_MAX_VOL = 255;
 
+    // BtDevice constants currently rolling out under flag protection. Use own
+    // constants instead to avoid mainline dependency from flag library import
+    // TODO(b/335936458): remove once the BtDevice flag is rolled out
+    private static final String DEVICE_TYPE_SPEAKER = "Speaker";
+    private static final String DEVICE_TYPE_HEADSET = "Headset";
+    private static final String DEVICE_TYPE_CARKIT = "Carkit";
+    private static final String DEVICE_TYPE_HEARING_AID = "HearingAid";
+
     /**
      * Returns a string representation of the scoAudioMode.
      */
@@ -401,50 +405,67 @@
     private void onScoAudioStateChanged(int state) {
         boolean broadcast = false;
         int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
-        switch (state) {
-            case BluetoothHeadset.STATE_AUDIO_CONNECTED:
-                scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
-                if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
-                        && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
-                    mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
-                } else if (mDeviceBroker.isBluetoothScoRequested()) {
-                    // broadcast intent if the connection was initated by AudioService
+        if (mDeviceBroker.isScoManagedByAudio()) {
+            switch (state) {
+                case BluetoothHeadset.STATE_AUDIO_CONNECTED:
+                    mDeviceBroker.setBluetoothScoOn(true, "BtHelper.onScoAudioStateChanged");
+                    scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
                     broadcast = true;
-                }
-                mDeviceBroker.setBluetoothScoOn(true, "BtHelper.onScoAudioStateChanged");
-                break;
-            case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
-                mDeviceBroker.setBluetoothScoOn(false, "BtHelper.onScoAudioStateChanged");
-                scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
-                // There are two cases where we want to immediately reconnect audio:
-                // 1) If a new start request was received while disconnecting: this was
-                // notified by requestScoState() setting state to SCO_STATE_ACTIVATE_REQ.
-                // 2) If audio was connected then disconnected via Bluetooth APIs and
-                // we still have pending activation requests by apps: this is indicated by
-                // state SCO_STATE_ACTIVE_EXTERNAL and BT SCO is requested.
-                if (mScoAudioState == SCO_STATE_ACTIVATE_REQ) {
-                    if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null
-                            && connectBluetoothScoAudioHelper(mBluetoothHeadset,
-                            mBluetoothHeadsetDevice, mScoAudioMode)) {
-                        mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
-                        scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTING;
+                    break;
+                case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
+                    mDeviceBroker.setBluetoothScoOn(false, "BtHelper.onScoAudioStateChanged");
+                    scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
+                    broadcast = true;
+                    break;
+                default:
+                    break;
+            }
+        } else {
+            switch (state) {
+                case BluetoothHeadset.STATE_AUDIO_CONNECTED:
+                    scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
+                    if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
+                            && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
+                        mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
+                    } else if (mDeviceBroker.isBluetoothScoRequested()) {
+                        // broadcast intent if the connection was initated by AudioService
                         broadcast = true;
-                        break;
                     }
-                }
-                if (mScoAudioState != SCO_STATE_ACTIVE_EXTERNAL) {
-                    broadcast = true;
-                }
-                mScoAudioState = SCO_STATE_INACTIVE;
-                break;
-            case BluetoothHeadset.STATE_AUDIO_CONNECTING:
-                if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
-                        && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
-                    mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
-                }
-                break;
-            default:
-                break;
+                    mDeviceBroker.setBluetoothScoOn(true, "BtHelper.onScoAudioStateChanged");
+                    break;
+                case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
+                    mDeviceBroker.setBluetoothScoOn(false, "BtHelper.onScoAudioStateChanged");
+                    scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
+                    // There are two cases where we want to immediately reconnect audio:
+                    // 1) If a new start request was received while disconnecting: this was
+                    // notified by requestScoState() setting state to SCO_STATE_ACTIVATE_REQ.
+                    // 2) If audio was connected then disconnected via Bluetooth APIs and
+                    // we still have pending activation requests by apps: this is indicated by
+                    // state SCO_STATE_ACTIVE_EXTERNAL and BT SCO is requested.
+                    if (mScoAudioState == SCO_STATE_ACTIVATE_REQ) {
+                        if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null
+                                && connectBluetoothScoAudioHelper(mBluetoothHeadset,
+                                mBluetoothHeadsetDevice, mScoAudioMode)) {
+                            mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+                            scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTING;
+                            broadcast = true;
+                            break;
+                        }
+                    }
+                    if (mScoAudioState != SCO_STATE_ACTIVE_EXTERNAL) {
+                        broadcast = true;
+                    }
+                    mScoAudioState = SCO_STATE_INACTIVE;
+                    break;
+                case BluetoothHeadset.STATE_AUDIO_CONNECTING:
+                    if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
+                            && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
+                        mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
+                    }
+                    break;
+                default:
+                    break;
+            }
         }
         if (broadcast) {
             broadcastScoConnectionState(scoAudioState);
@@ -454,7 +475,6 @@
             newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState);
             sendStickyBroadcastToAll(newIntent);
         }
-
     }
     /**
      *
@@ -577,7 +597,11 @@
                 mHearingAid = null;
                 break;
             case BluetoothProfile.LE_AUDIO:
+                if (mLeAudio != null && mLeAudioCallback != null) {
+                    mLeAudio.unregisterCallback(mLeAudioCallback);
+                }
                 mLeAudio = null;
+                mLeAudioCallback = null;
                 mLeAudioCodecConfig = null;
                 break;
             case BluetoothProfile.LE_AUDIO_BROADCAST:
@@ -596,8 +620,6 @@
 
     // BluetoothLeAudio callback used to update the list of addresses in the same group as a
     // connected LE Audio device
-    MyLeAudioCallback mLeAudioCallback = null;
-
     class MyLeAudioCallback implements BluetoothLeAudio.Callback {
         @Override
         public void onCodecConfigChanged(int groupId,
@@ -620,6 +642,8 @@
         }
     }
 
+    MyLeAudioCallback mLeAudioCallback = null;
+
     // @GuardedBy("mDeviceBroker.mSetModeLock")
     @GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
     /*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) {
@@ -635,18 +659,28 @@
                 onHeadsetProfileConnected((BluetoothHeadset) proxy);
                 return;
             case BluetoothProfile.A2DP:
+                if (((BluetoothA2dp) proxy).equals(mA2dp)) {
+                    return;
+                }
                 mA2dp = (BluetoothA2dp) proxy;
                 break;
             case BluetoothProfile.HEARING_AID:
+                if (((BluetoothHearingAid) proxy).equals(mHearingAid)) {
+                    return;
+                }
                 mHearingAid = (BluetoothHearingAid) proxy;
                 break;
             case BluetoothProfile.LE_AUDIO:
-                if (mLeAudio == null) {
-                    mLeAudioCallback = new MyLeAudioCallback();
-                    ((BluetoothLeAudio) proxy).registerCallback(
-                            mContext.getMainExecutor(), mLeAudioCallback);
+                if (((BluetoothLeAudio) proxy).equals(mLeAudio)) {
+                    return;
+                }
+                if (mLeAudio != null && mLeAudioCallback != null) {
+                    mLeAudio.unregisterCallback(mLeAudioCallback);
                 }
                 mLeAudio = (BluetoothLeAudio) proxy;
+                mLeAudioCallback = new MyLeAudioCallback();
+                mLeAudio.registerCallback(
+                            mContext.getMainExecutor(), mLeAudioCallback);
                 break;
             case BluetoothProfile.A2DP_SINK:
             case BluetoothProfile.LE_AUDIO_BROADCAST:
diff --git a/services/core/java/com/android/server/audio/DefaultAudioPolicyFacade.java b/services/core/java/com/android/server/audio/DefaultAudioPolicyFacade.java
index 37b8126..09701e4 100644
--- a/services/core/java/com/android/server/audio/DefaultAudioPolicyFacade.java
+++ b/services/core/java/com/android/server/audio/DefaultAudioPolicyFacade.java
@@ -16,100 +16,68 @@
 
 package com.android.server.audio;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.media.IAudioPolicyService;
-import android.media.permission.ClearCallingIdentityContext;
-import android.media.permission.SafeCloseable;
+import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.util.Log;
 
-import com.android.internal.annotations.GuardedBy;
+import com.android.media.permission.INativePermissionController;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Function;
 
 /**
- * Default implementation of a facade to IAudioPolicyManager which fulfills AudioService
- * dependencies. This forwards calls as-is to IAudioPolicyManager.
- * Public methods throw IllegalStateException if AudioPolicy is not initialized/available
+ * Default implementation of a facade to IAudioPolicyService which fulfills AudioService
+ * dependencies. This forwards calls as-is to IAudioPolicyService.
  */
-public class DefaultAudioPolicyFacade implements AudioPolicyFacade, IBinder.DeathRecipient {
+public class DefaultAudioPolicyFacade implements AudioPolicyFacade {
 
-    private static final String TAG = "DefaultAudioPolicyFacade";
     private static final String AUDIO_POLICY_SERVICE_NAME = "media.audio_policy";
 
-    private final Object mServiceLock = new Object();
-    @GuardedBy("mServiceLock")
-    private IAudioPolicyService mAudioPolicy;
+    private final ServiceHolder<IAudioPolicyService> mServiceHolder;
 
-    public DefaultAudioPolicyFacade() {
-        try {
-            getAudioPolicyOrInit();
-        } catch (IllegalStateException e) {
-            // Log and suppress this exception, we may be able to connect later
-            Log.e(TAG, "Failed to initialize APM connection", e);
-        }
+    /**
+     * @param e - Executor for service start tasks
+     */
+    public DefaultAudioPolicyFacade(Executor e) {
+        mServiceHolder =
+                new ServiceHolder(
+                        AUDIO_POLICY_SERVICE_NAME,
+                        (Function<IBinder, IAudioPolicyService>)
+                                IAudioPolicyService.Stub::asInterface,
+                        e);
+        mServiceHolder.registerOnStartTask(i -> Binder.allowBlocking(i.asBinder()));
     }
 
     @Override
     public boolean isHotwordStreamSupported(boolean lookbackAudio) {
-        IAudioPolicyService ap = getAudioPolicyOrInit();
-        try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+        IAudioPolicyService ap = mServiceHolder.waitForService();
+        try {
             return ap.isHotwordStreamSupported(lookbackAudio);
         } catch (RemoteException e) {
-            resetServiceConnection(ap.asBinder());
-            throw new IllegalStateException(e);
+            mServiceHolder.attemptClear(ap.asBinder());
+            throw new IllegalStateException();
         }
     }
 
     @Override
-    public void binderDied() {
-        Log.wtf(TAG, "Unexpected binderDied without IBinder object");
+    public @Nullable INativePermissionController getPermissionController() {
+        IAudioPolicyService ap = mServiceHolder.checkService();
+        if (ap == null) return null;
+        try {
+            var res = Objects.requireNonNull(ap.getPermissionController());
+            Binder.allowBlocking(res.asBinder());
+            return res;
+        } catch (RemoteException e) {
+            mServiceHolder.attemptClear(ap.asBinder());
+            return null;
+        }
     }
 
     @Override
-    public void binderDied(@NonNull IBinder who) {
-        resetServiceConnection(who);
-    }
-
-    private void resetServiceConnection(@Nullable IBinder deadAudioPolicy) {
-        synchronized (mServiceLock) {
-            if (mAudioPolicy != null && mAudioPolicy.asBinder().equals(deadAudioPolicy)) {
-                mAudioPolicy.asBinder().unlinkToDeath(this, 0);
-                mAudioPolicy = null;
-            }
-        }
-    }
-
-    private @Nullable IAudioPolicyService getAudioPolicy() {
-        synchronized (mServiceLock) {
-            return mAudioPolicy;
-        }
-    }
-
-    /*
-     * Does not block.
-     * @throws IllegalStateException for any failed connection
-     */
-    private @NonNull IAudioPolicyService getAudioPolicyOrInit() {
-        synchronized (mServiceLock) {
-            if (mAudioPolicy != null) {
-                return mAudioPolicy;
-            }
-            // Do not block while attempting to connect to APM. Defer to caller.
-            IAudioPolicyService ap = IAudioPolicyService.Stub.asInterface(
-                    ServiceManager.checkService(AUDIO_POLICY_SERVICE_NAME));
-            if (ap == null) {
-                throw new IllegalStateException(TAG + ": Unable to connect to AudioPolicy");
-            }
-            try {
-                ap.asBinder().linkToDeath(this, 0);
-            } catch (RemoteException e) {
-                throw new IllegalStateException(
-                        TAG + ": Unable to link deathListener to AudioPolicy", e);
-            }
-            mAudioPolicy = ap;
-            return mAudioPolicy;
-        }
+    public void registerOnStartTask(Runnable task) {
+        mServiceHolder.registerOnStartTask(unused -> task.run());
     }
 }
diff --git a/services/core/java/com/android/server/audio/ServiceHolder.java b/services/core/java/com/android/server/audio/ServiceHolder.java
new file mode 100644
index 0000000..e2588fb
--- /dev/null
+++ b/services/core/java/com/android/server/audio/ServiceHolder.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.audio;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.IServiceCallback;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+/**
+ * Manages a remote service which can start and stop. Allows clients to add tasks to run when the
+ * remote service starts or dies.
+ *
+ * <p>Example usage should look something like:
+ *
+ * <pre>
+ *      var service = mServiceHolder.checkService();
+ *      if (service == null) handleFailure();
+ *      try {
+ *          service.foo();
+ *      } catch (RemoteException e) {
+ *          mServiceHolder.attemptClear(service.asBinder());
+ *          handleFailure();
+ *      }
+ * </pre>
+ */
+public class ServiceHolder<I extends IInterface> implements IBinder.DeathRecipient {
+
+    private final String mTag;
+    private final String mServiceName;
+    private final Function<? super IBinder, ? extends I> mCastFunction;
+    private final Executor mExecutor;
+    private final ServiceProviderFacade mServiceProvider;
+
+    private final AtomicReference<I> mService = new AtomicReference();
+    private final Set<Consumer<I>> mOnStartTasks = ConcurrentHashMap.newKeySet();
+    private final Set<Consumer<I>> mOnDeathTasks = ConcurrentHashMap.newKeySet();
+
+    private final IServiceCallback mServiceListener =
+            new IServiceCallback.Stub() {
+                @Override
+                public void onRegistration(String name, IBinder binder) {
+                    onServiceInited(binder);
+                }
+            };
+
+    // For test purposes
+    public static interface ServiceProviderFacade {
+        public void registerForNotifications(String name, IServiceCallback listener);
+
+        public IBinder checkService(String name);
+
+        public IBinder waitForService(String name);
+    }
+
+    public ServiceHolder(
+            @NonNull String serviceName,
+            @NonNull Function<? super IBinder, ? extends I> castFunction,
+            @NonNull Executor executor) {
+        this(
+                serviceName,
+                castFunction,
+                executor,
+                new ServiceProviderFacade() {
+                    @Override
+                    public void registerForNotifications(String name, IServiceCallback listener) {
+                        try {
+                            ServiceManager.registerForNotifications(name, listener);
+                        } catch (RemoteException e) {
+                            throw new IllegalStateException("ServiceManager died!!", e);
+                        }
+                    }
+
+                    @Override
+                    public IBinder checkService(String name) {
+                        return ServiceManager.checkService(name);
+                    }
+
+                    @Override
+                    public IBinder waitForService(String name) {
+                        return ServiceManager.waitForService(name);
+                    }
+                });
+    }
+
+    public ServiceHolder(
+            @NonNull String serviceName,
+            @NonNull Function<? super IBinder, ? extends I> castFunction,
+            @NonNull Executor executor,
+            @NonNull ServiceProviderFacade provider) {
+        mServiceName = Objects.requireNonNull(serviceName);
+        mCastFunction = Objects.requireNonNull(castFunction);
+        mExecutor = Objects.requireNonNull(executor);
+        mServiceProvider = Objects.requireNonNull(provider);
+        mTag = "ServiceHolder: " + serviceName;
+        mServiceProvider.registerForNotifications(mServiceName, mServiceListener);
+    }
+
+    /**
+     * Add tasks to run when service becomes available. Ran on the executor provided at
+     * construction. Note, for convenience, if the service is already connected, the task is
+     * immediately run.
+     */
+    public void registerOnStartTask(Consumer<I> task) {
+        mOnStartTasks.add(task);
+        I i;
+        if ((i = mService.get()) != null) {
+            mExecutor.execute(() -> task.accept(i));
+        }
+    }
+
+    public void unregisterOnStartTask(Consumer<I> task) {
+        mOnStartTasks.remove(task);
+    }
+
+    /**
+     * Add tasks to run when service goes down. Ran on the executor provided at construction. Should
+     * be called before getService to avoid dropping a death notification.
+     */
+    public void registerOnDeathTask(Consumer<I> task) {
+        mOnDeathTasks.add(task);
+    }
+
+    public void unregisterOnDeathTask(Consumer<I> task) {
+        mOnDeathTasks.remove(task);
+    }
+
+    @Override
+    public void binderDied(@NonNull IBinder who) {
+        attemptClear(who);
+    }
+
+    @Override
+    public void binderDied() {
+        throw new AssertionError("Wrong binderDied called, this should never happen");
+    }
+
+    /**
+     * Notify the holder that the service has gone done, usually in response to a RemoteException.
+     * Equivalent to receiving a binder death notification.
+     */
+    public void attemptClear(IBinder who) {
+        // Possibly prone to weird races, resulting in spurious dead/revive,
+        // but that should be fine.
+        var current = mService.get();
+        if (current != null
+                && Objects.equals(current.asBinder(), who)
+                && mService.compareAndSet(current, null)) {
+            who.unlinkToDeath(this, 0);
+            for (var r : mOnDeathTasks) {
+                mExecutor.execute(() -> r.accept(current));
+            }
+        }
+    }
+
+    /** Get the service, without blocking. Can trigger start tasks, on the provided executor. */
+    public @Nullable I checkService() {
+        var s = mService.get();
+        if (s != null) return s;
+        IBinder registered = mServiceProvider.checkService(mServiceName);
+        if (registered == null) return null;
+        return onServiceInited(registered);
+    }
+
+    /** Get the service, but block. Can trigger start tasks, on the provided executor. */
+    public @NonNull I waitForService() {
+        var s = mService.get();
+        return (s != null) ? s : onServiceInited(mServiceProvider.waitForService(mServiceName));
+    }
+
+    /*
+     * Called when the native service is initialized.
+     */
+    private @NonNull I onServiceInited(@NonNull IBinder who) {
+        var service = mCastFunction.apply(who);
+        Objects.requireNonNull(service);
+        if (!mService.compareAndSet(null, service)) {
+            return service;
+        }
+        // Even if the service has immediately died, we should perform these tasks for consistency
+        for (var r : mOnStartTasks) {
+            mExecutor.execute(() -> r.accept(service));
+        }
+        try {
+            who.linkToDeath(this, 0);
+        } catch (RemoteException e) {
+            Log.e(mTag, "Immediate service death. Service crash-looping");
+            attemptClear(who);
+        }
+        // This interface is non-null, but could represent a dead object
+        return service;
+    }
+}
diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java
index 9610034ca..e28ae95 100644
--- a/services/core/java/com/android/server/audio/SoundDoseHelper.java
+++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java
@@ -856,11 +856,12 @@
         pw.println();
     }
 
-    /*package*/void  reset() {
+    /*package*/void reset(boolean resetISoundDose) {
         Log.d(TAG, "Reset the sound dose helper");
 
-        mSoundDose.compareAndExchange(/*expectedValue=*/null,
-                AudioSystem.getSoundDoseInterface(mSoundDoseCallback));
+        if (resetISoundDose) {
+            mSoundDose.set(AudioSystem.getSoundDoseInterface(mSoundDoseCallback));
+        }
 
         synchronized (mCsdStateLock) {
             try {
@@ -972,7 +973,7 @@
             }
         }
 
-        reset();
+        reset(/*resetISoundDose=*/false);
     }
 
     private void onConfigureSafeMedia(boolean force, String caller) {
diff --git a/services/core/java/com/android/server/biometrics/biometrics.aconfig b/services/core/java/com/android/server/biometrics/biometrics.aconfig
index 712dcee..92fd9cb 100644
--- a/services/core/java/com/android/server/biometrics/biometrics.aconfig
+++ b/services/core/java/com/android/server/biometrics/biometrics.aconfig
@@ -14,10 +14,3 @@
   description: "This flag controls whether virtual HAL is used for testing instead of TestHal "
   bug: "294254230"
 }
-
-flag {
-  name: "mandatory_biometrics"
-  namespace: "biometrics_framework"
-  description: "This flag controls whether LSKF fallback is removed from biometric prompt when the phone is outside trusted locations"
-  bug: "322081563"
-}
diff --git a/services/core/java/com/android/server/display/DisplayAdapter.java b/services/core/java/com/android/server/display/DisplayAdapter.java
index 5690a9e..69bc66f 100644
--- a/services/core/java/com/android/server/display/DisplayAdapter.java
+++ b/services/core/java/com/android/server/display/DisplayAdapter.java
@@ -135,7 +135,7 @@
             float[] alternativeRefreshRates,
             @Display.HdrCapabilities.HdrType int[] supportedHdrTypes) {
         return new Display.Mode(NEXT_DISPLAY_MODE_ID.getAndIncrement(), width, height, refreshRate,
-                vsyncRate, false, alternativeRefreshRates, supportedHdrTypes);
+                vsyncRate, /* isSynthetic= */ false, alternativeRefreshRates, supportedHdrTypes);
     }
 
     public interface Listener {
diff --git a/services/core/java/com/android/server/display/DisplayBrightnessState.java b/services/core/java/com/android/server/display/DisplayBrightnessState.java
index baa154d..184ae41 100644
--- a/services/core/java/com/android/server/display/DisplayBrightnessState.java
+++ b/services/core/java/com/android/server/display/DisplayBrightnessState.java
@@ -47,6 +47,8 @@
     private final BrightnessEvent mBrightnessEvent;
     private final int mBrightnessAdjustmentFlag;
 
+    private final boolean mIsUserInitiatedChange;
+
     private DisplayBrightnessState(Builder builder) {
         mBrightness = builder.getBrightness();
         mSdrBrightness = builder.getSdrBrightness();
@@ -60,6 +62,7 @@
         mShouldUpdateScreenBrightnessSetting = builder.shouldUpdateScreenBrightnessSetting();
         mBrightnessEvent = builder.getBrightnessEvent();
         mBrightnessAdjustmentFlag = builder.getBrightnessAdjustmentFlag();
+        mIsUserInitiatedChange = builder.isUserInitiatedChange();
     }
 
     /**
@@ -148,6 +151,13 @@
         return mBrightnessAdjustmentFlag;
     }
 
+    /**
+     * Gets if the current brightness changes are because of a user initiated change
+     */
+    public boolean isUserInitiatedChange() {
+        return mIsUserInitiatedChange;
+    }
+
     @Override
     public String toString() {
         StringBuilder stringBuilder = new StringBuilder("DisplayBrightnessState:");
@@ -168,6 +178,7 @@
         stringBuilder.append("\n    mBrightnessEvent:")
                 .append(Objects.toString(mBrightnessEvent, "null"));
         stringBuilder.append("\n    mBrightnessAdjustmentFlag:").append(mBrightnessAdjustmentFlag);
+        stringBuilder.append("\n    mIsUserInitiatedChange:").append(mIsUserInitiatedChange);
         return stringBuilder.toString();
     }
 
@@ -199,7 +210,8 @@
                 && mShouldUpdateScreenBrightnessSetting
                     == otherState.shouldUpdateScreenBrightnessSetting()
                 && Objects.equals(mBrightnessEvent, otherState.getBrightnessEvent())
-                && mBrightnessAdjustmentFlag == otherState.getBrightnessAdjustmentFlag();
+                && mBrightnessAdjustmentFlag == otherState.getBrightnessAdjustmentFlag()
+                && mIsUserInitiatedChange == otherState.isUserInitiatedChange();
     }
 
     @Override
@@ -207,7 +219,8 @@
         return Objects.hash(mBrightness, mSdrBrightness, mBrightnessReason,
                 mShouldUseAutoBrightness, mIsSlowChange, mMaxBrightness, mMinBrightness,
                 mCustomAnimationRate,
-                mShouldUpdateScreenBrightnessSetting, mBrightnessEvent, mBrightnessAdjustmentFlag);
+                mShouldUpdateScreenBrightnessSetting, mBrightnessEvent, mBrightnessAdjustmentFlag,
+                mIsUserInitiatedChange);
     }
 
     /**
@@ -236,6 +249,8 @@
 
         public int mBrightnessAdjustmentFlag = 0;
 
+        private boolean mIsUserInitiatedChange;
+
         /**
          * Create a builder starting with the values from the specified {@link
          * DisplayBrightnessState}.
@@ -257,6 +272,7 @@
                     state.shouldUpdateScreenBrightnessSetting());
             builder.setBrightnessEvent(state.getBrightnessEvent());
             builder.setBrightnessAdjustmentFlag(state.getBrightnessAdjustmentFlag());
+            builder.setIsUserInitiatedChange(state.isUserInitiatedChange());
             return builder;
         }
 
@@ -464,5 +480,20 @@
             mBrightnessAdjustmentFlag = brightnessAdjustmentFlag;
             return this;
         }
+
+        /**
+         * Gets if the current change is a user initiated change
+         */
+        public boolean isUserInitiatedChange() {
+            return mIsUserInitiatedChange;
+        }
+
+        /**
+         * This is used to set if the current change is a user initiated change
+         */
+        public Builder setIsUserInitiatedChange(boolean isUserInitiatedChange) {
+            mIsUserInitiatedChange = isUserInitiatedChange;
+            return this;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 27ea1cd..5fd0253 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -49,6 +49,8 @@
 import static android.provider.Settings.Secure.RESOLUTION_MODE_HIGH;
 import static android.provider.Settings.Secure.RESOLUTION_MODE_UNKNOWN;
 
+import static com.android.server.display.layout.Layout.Display.POSITION_REAR;
+
 import android.Manifest;
 import android.annotation.EnforcePermission;
 import android.annotation.NonNull;
@@ -1745,6 +1747,7 @@
                 if (projection != null) {
                     IBinder taskWindowContainerToken = projection.getLaunchCookie() == null ? null
                             : projection.getLaunchCookie().binder;
+                    int taskId = projection.getTaskId();
                     if (taskWindowContainerToken == null) {
                         // Record a particular display.
                         session = ContentRecordingSession.createDisplaySession(
@@ -1752,7 +1755,7 @@
                     } else {
                         // Record a single task indicated by the launch cookie.
                         session = ContentRecordingSession.createTaskSession(
-                                taskWindowContainerToken);
+                                taskWindowContainerToken, taskId);
                     }
                 }
             } catch (RemoteException e) {
@@ -4971,8 +4974,9 @@
                 }
 
                 final DisplayDevice displayDevice = display.getPrimaryDisplayDeviceLocked();
-                final boolean ownContent = (displayDevice.getDisplayDeviceInfoLocked().flags
-                        & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY) != 0;
+                final boolean isRearDisplay = display.getDevicePositionLocked() == POSITION_REAR;
+                final boolean ownContent = ((displayDevice.getDisplayDeviceInfoLocked().flags
+                        & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY) != 0) || isRearDisplay;
                 // If the display has enabled mirroring, but specified that it will be managed by
                 // WindowManager, return an invalid display id. This is to ensure we don't
                 // accidentally select the display id to mirror based on DM logic and instead allow
diff --git a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
index f77a360..0fef55d 100644
--- a/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
+++ b/services/core/java/com/android/server/display/DisplayOffloadSessionImpl.java
@@ -21,13 +21,21 @@
 import android.annotation.Nullable;
 import android.hardware.display.DisplayManagerInternal;
 import android.os.Trace;
+import android.util.Slog;
 import android.view.Display;
 
+import com.android.server.display.utils.DebugUtils;
+
 /**
  * An implementation of the offload session that keeps track of whether the session is active.
  * An offload session is used to control the display's brightness using the offload chip.
  */
 public class DisplayOffloadSessionImpl implements DisplayManagerInternal.DisplayOffloadSession {
+    private static final String TAG = "DisplayOffloadSessionImpl";
+
+    // To enable these logs, run:
+    // 'adb shell setprop persist.log.tag.DisplayOffloadSessionImpl DEBUG && adb reboot'
+    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
 
     @Nullable
     private final DisplayManagerInternal.DisplayOffloader mDisplayOffloader;
@@ -99,9 +107,14 @@
         if (mDisplayOffloader == null || mIsActive) {
             return false;
         }
+
         Trace.traceBegin(Trace.TRACE_TAG_POWER, "DisplayOffloader#startOffload");
         try {
-            return mIsActive = mDisplayOffloader.startOffload();
+            mIsActive = mDisplayOffloader.startOffload();
+            if (DEBUG) {
+                Slog.d(TAG, "startOffload = " + mIsActive);
+            }
+            return mIsActive;
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_POWER);
         }
@@ -118,6 +131,9 @@
         try {
             mDisplayOffloader.stopOffload();
             mIsActive = false;
+            if (DEBUG) {
+                Slog.i(TAG, "stopOffload");
+            }
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_POWER);
         }
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 7d482f7..8d71c70 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -791,10 +791,11 @@
 
     @Override
     public void overrideDozeScreenState(int displayState, @Display.StateReason int reason) {
+        Slog.i(TAG, "New offload doze override: " + Display.stateToString(displayState));
         mHandler.postAtTime(() -> {
             if (mDisplayOffloadSession == null
                     || !(DisplayOffloadSession.isSupportedOffloadState(displayState)
-                    || displayState == Display.STATE_UNKNOWN)) {
+                            || displayState == Display.STATE_UNKNOWN)) {
                 return;
             }
             mDisplayStateController.overrideDozeScreenState(displayState, reason);
@@ -1279,7 +1280,7 @@
 
     private void updatePowerStateInternal() {
         // Update the power state request.
-        final boolean mustNotify;
+        boolean mustNotify = false;
         final int previousPolicy;
         boolean mustInitialize = false;
         mBrightnessReasonTemp.set(null);
@@ -1327,6 +1328,30 @@
             initialize(readyToUpdateDisplayState() ? state : Display.STATE_UNKNOWN);
         }
 
+        if (mFlags.isOffloadDozeOverrideHoldsWakelockEnabled()) {
+            // Sometimes, a display-state change can come without an associated PowerRequest,
+            // as with DisplayOffload.  For those cases, we have to make sure to also mark the
+            // display as "not ready" so that we can inform power-manager when the state-change is
+            // complete.
+            if (mPowerState.getScreenState() != state) {
+                final boolean wasReady;
+                synchronized (mLock) {
+                    wasReady = mDisplayReadyLocked;
+                    mDisplayReadyLocked = false;
+                    mustNotify = true;
+                }
+
+                if (wasReady) {
+                    // If we went from ready to not-ready from the state-change (instead of a
+                    // PowerRequest) there's a good chance that nothing is keeping PowerManager
+                    // from suspending. Grab the unfinished business suspend blocker to keep the
+                    // device awake until the display-state change goes into effect.
+                    mWakelockController.acquireWakelock(
+                            WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS);
+                }
+            }
+        }
+
         // Animate the screen state change unless already animating.
         // The transition may be deferred, so after this point we will use the
         // actual state instead of the desired one.
@@ -1366,6 +1391,7 @@
         // request changes.
         final boolean wasShortTermModelActive =
                 mAutomaticBrightnessStrategy.isShortTermModelActive();
+        boolean userInitiatedChange = displayBrightnessState.isUserInitiatedChange();
         boolean allowAutoBrightnessWhileDozing =
                 mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig();
         if (mFlags.offloadControlsDozeAutoBrightness() && mFlags.isDisplayOffloadEnabled()
@@ -1388,13 +1414,14 @@
                     mPowerRequest.policy,
                     mDisplayBrightnessController.getLastUserSetScreenBrightness(),
                     userSetBrightnessChanged);
+
+            // If the brightness is already set then it's been overridden by something other than
+            // the user, or is a temporary adjustment.
+            userInitiatedChange = (Float.isNaN(brightnessState))
+                    && (mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentChanged()
+                    || userSetBrightnessChanged);
         }
 
-        // If the brightness is already set then it's been overridden by something other than the
-        // user, or is a temporary adjustment.
-        boolean userInitiatedChange = (Float.isNaN(brightnessState))
-                && (mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentChanged()
-                || userSetBrightnessChanged);
 
         final int autoBrightnessState = mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()
                 ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 182b05a..44846f3 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -168,6 +168,12 @@
             }
             SurfaceControl.DesiredDisplayModeSpecs modeSpecs =
                     mSurfaceControlProxy.getDesiredDisplayModeSpecs(displayToken);
+            if (modeSpecs == null) {
+                // If mode specs is null, it most probably means that display got
+                // unplugged very rapidly.
+                Slog.w(TAG, "Desired display mode specs from SurfaceFlinger are null");
+                return;
+            }
             LocalDisplayDevice device = mDevices.get(physicalDisplayId);
             if (device == null) {
                 // Display was added.
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index aa17df6..d567331 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -600,6 +600,7 @@
     private StrategyExecutionRequest constructStrategyExecutionRequest(
             DisplayManagerInternal.DisplayPowerRequest displayPowerRequest) {
         float currentScreenBrightness = getCurrentBrightness();
-        return new StrategyExecutionRequest(displayPowerRequest, currentScreenBrightness);
+        return new StrategyExecutionRequest(displayPowerRequest, currentScreenBrightness,
+                mUserSetScreenBrightnessUpdated);
     }
 }
diff --git a/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java b/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java
index 82c0bbf..304640b 100644
--- a/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java
+++ b/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java
@@ -29,10 +29,14 @@
 
     private final float mCurrentScreenBrightness;
 
+    // Represents if the user set screen brightness was changed or not.
+    private boolean mUserSetBrightnessChanged;
+
     public StrategyExecutionRequest(DisplayManagerInternal.DisplayPowerRequest displayPowerRequest,
-            float currentScreenBrightness) {
+            float currentScreenBrightness, boolean userSetBrightnessChanged) {
         mDisplayPowerRequest = displayPowerRequest;
         mCurrentScreenBrightness = currentScreenBrightness;
+        mUserSetBrightnessChanged = userSetBrightnessChanged;
     }
 
     public DisplayManagerInternal.DisplayPowerRequest getDisplayPowerRequest() {
@@ -43,6 +47,10 @@
         return mCurrentScreenBrightness;
     }
 
+    public boolean isUserSetBrightnessChanged() {
+        return mUserSetBrightnessChanged;
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (!(obj instanceof StrategyExecutionRequest)) {
@@ -50,11 +58,13 @@
         }
         StrategyExecutionRequest other = (StrategyExecutionRequest) obj;
         return Objects.equals(mDisplayPowerRequest, other.getDisplayPowerRequest())
-                && mCurrentScreenBrightness == other.getCurrentScreenBrightness();
+                && mCurrentScreenBrightness == other.getCurrentScreenBrightness()
+                && mUserSetBrightnessChanged == other.isUserSetBrightnessChanged();
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mDisplayPowerRequest, mCurrentScreenBrightness);
+        return Objects.hash(mDisplayPowerRequest, mCurrentScreenBrightness,
+                mUserSetBrightnessChanged);
     }
 }
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
index 2907364..2b5241f 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
@@ -290,6 +290,8 @@
                 .setBrightnessAdjustmentFlag(mAutoBrightnessAdjustmentReasonsFlags)
                 .setShouldUpdateScreenBrightnessSetting(
                         brightness != strategyExecutionRequest.getCurrentScreenBrightness())
+                .setIsUserInitiatedChange(getAutoBrightnessAdjustmentChanged()
+                        || strategyExecutionRequest.isUserSetBrightnessChanged())
                 .build();
     }
 
diff --git a/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
index 3463649a..0b92317 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
@@ -45,6 +45,7 @@
                 // The fallback brightness might change due to clamping. Make sure we tell the rest
                 // of the system by updating the setting
                 .setShouldUpdateScreenBrightnessSetting(true)
+                .setIsUserInitiatedChange(strategyExecutionRequest.isUserSetBrightnessChanged())
                 .build();
     }
 
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 8f775a5..f56d803 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -174,6 +174,11 @@
             Flags::enableSynthetic60hzModes
     );
 
+    private final FlagState mOffloadDozeOverrideHoldsWakelock = new FlagState(
+            Flags.FLAG_OFFLOAD_DOZE_OVERRIDE_HOLDS_WAKELOCK,
+            Flags::offloadDozeOverrideHoldsWakelock
+    );
+
     /**
      * @return {@code true} if 'port' is allowed in display layout configuration file.
      */
@@ -343,8 +348,12 @@
         return mPeakRefreshRatePhysicalLimit.isEnabled();
     }
 
+    public boolean isOffloadDozeOverrideHoldsWakelockEnabled() {
+        return mOffloadDozeOverrideHoldsWakelock.isEnabled();
+    }
+
     /**
-     * @return Whether to ignore preferredRefreshRate app request or not
+     * @return Whether to ignore preferredRefreshRate app request conversion to display mode or not
      */
     public boolean ignoreAppPreferredRefreshRateRequest() {
         return mIgnoreAppPreferredRefreshRate.isEnabled();
@@ -389,6 +398,7 @@
         pw.println(" " + mPeakRefreshRatePhysicalLimit);
         pw.println(" " + mIgnoreAppPreferredRefreshRate);
         pw.println(" " + mSynthetic60hzModes);
+        pw.println(" " + mOffloadDozeOverrideHoldsWakelock);
     }
 
     private static class FlagState {
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 697218d..95d0ca3 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
@@ -289,3 +289,13 @@
     }
 }
 
+flag {
+    name: "offload_doze_override_holds_wakelock"
+    namespace: "display_manager"
+    description: "DisplayPowerController holds a suspend-blocker while changing the display state on behalf of offload doze override."
+    bug: "338403827"
+    is_fixed_read_only: true
+    metadata {
+      purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 76a2827..d519748 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -1254,13 +1254,9 @@
      *  Responsible for keeping track of app requested refresh rates per display
      */
     public final class AppRequestObserver {
-        private final SparseArray<Display.Mode> mAppRequestedModeByDisplay;
-        private final SparseArray<RefreshRateRange> mAppPreferredRefreshRateRangeByDisplay;
         private final boolean mIgnorePreferredRefreshRate;
 
         AppRequestObserver(DisplayManagerFlags flags) {
-            mAppRequestedModeByDisplay = new SparseArray<>();
-            mAppPreferredRefreshRateRangeByDisplay = new SparseArray<>();
             mIgnorePreferredRefreshRate = flags.ignoreAppPreferredRefreshRateRequest();
         }
 
@@ -1269,34 +1265,77 @@
          */
         public void setAppRequest(int displayId, int modeId, float requestedRefreshRate,
                 float requestedMinRefreshRateRange, float requestedMaxRefreshRateRange) {
+            Display.Mode requestedMode;
+            synchronized (mLock) {
+                requestedMode = findModeLocked(displayId, modeId, requestedRefreshRate);
+            }
 
-            if (modeId == 0 && requestedRefreshRate != 0 && !mIgnorePreferredRefreshRate) {
+            Vote frameRateVote = getFrameRateVote(
+                    requestedMinRefreshRateRange, requestedMaxRefreshRateRange);
+            Vote baseModeRefreshRateVote = getBaseModeVote(requestedMode, requestedRefreshRate);
+            Vote sizeVote = getSizeVote(requestedMode);
+
+            mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE,
+                    frameRateVote);
+            mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+                    baseModeRefreshRateVote);
+            mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
+        }
+
+        private Display.Mode findModeLocked(int displayId, int modeId, float requestedRefreshRate) {
+            Display.Mode mode = null;
+            if (modeId != 0) {
+                mode = findAppModeByIdLocked(displayId, modeId);
+            } else if (requestedRefreshRate != 0 && !mIgnorePreferredRefreshRate) { // modeId == 0
                 // Scan supported modes returned to find a mode with the same
                 // size as the default display mode but with the specified refresh rate instead.
-                Display.Mode mode = findDefaultModeByRefreshRate(displayId, requestedRefreshRate);
-                if (mode != null) {
-                    modeId = mode.getModeId();
-                } else {
+                mode = findDefaultModeByRefreshRateLocked(displayId, requestedRefreshRate);
+                if (mode == null) {
                     Slog.e(TAG, "Couldn't find a mode for the requestedRefreshRate: "
                             + requestedRefreshRate + " on Display: " + displayId);
                 }
             }
+            return mode;
+        }
 
-            synchronized (mLock) {
-                setAppRequestedModeLocked(displayId, modeId);
-                setAppPreferredRefreshRateRangeLocked(displayId, requestedMinRefreshRateRange,
-                        requestedMaxRefreshRateRange);
+        private Vote getFrameRateVote(float minRefreshRate, float maxRefreshRate) {
+            RefreshRateRange refreshRateRange = null;
+            if (minRefreshRate > 0 || maxRefreshRate > 0) {
+                float max = maxRefreshRate > 0
+                        ? maxRefreshRate : Float.POSITIVE_INFINITY;
+                refreshRateRange = new RefreshRateRange(minRefreshRate, max);
+                if (refreshRateRange.min == 0 && refreshRateRange.max == 0) {
+                    // minRefreshRate/maxRefreshRate were invalid
+                    refreshRateRange = null;
+                }
             }
+            return refreshRateRange != null
+                    ? Vote.forRenderFrameRates(refreshRateRange.min, refreshRateRange.max) : null;
+        }
+
+        private Vote getSizeVote(@Nullable Display.Mode mode) {
+            return mode != null
+                    ?  Vote.forSize(mode.getPhysicalWidth(), mode.getPhysicalHeight()) : null;
+        }
+
+        private Vote getBaseModeVote(@Nullable Display.Mode mode, float requestedRefreshRate) {
+            Vote vote = null;
+            if (mode != null) {
+                if (mode.isSynthetic()) {
+                    vote = Vote.forRequestedRefreshRate(mode.getRefreshRate());
+                } else {
+                    vote = Vote.forBaseModeRefreshRate(mode.getRefreshRate());
+                }
+            } else if (requestedRefreshRate != 0f && mIgnorePreferredRefreshRate) {
+                vote = Vote.forRequestedRefreshRate(requestedRefreshRate);
+            } // !mIgnorePreferredRefreshRate case is handled by findModeLocked
+            return vote;
         }
 
         @Nullable
-        private Display.Mode findDefaultModeByRefreshRate(int displayId, float refreshRate) {
-            Display.Mode[] modes;
-            Display.Mode defaultMode;
-            synchronized (mLock) {
-                modes = mAppSupportedModesByDisplay.get(displayId);
-                defaultMode = mDefaultModeByDisplay.get(displayId);
-            }
+        private Display.Mode findDefaultModeByRefreshRateLocked(int displayId, float refreshRate) {
+            Display.Mode[] modes = mAppSupportedModesByDisplay.get(displayId);
+            Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId);
             for (int i = 0; i < modes.length; i++) {
                 if (modes[i].matches(defaultMode.getPhysicalWidth(),
                         defaultMode.getPhysicalHeight(), refreshRate)) {
@@ -1306,69 +1345,6 @@
             return null;
         }
 
-        private void setAppRequestedModeLocked(int displayId, int modeId) {
-            final Display.Mode requestedMode = findAppModeByIdLocked(displayId, modeId);
-            if (Objects.equals(requestedMode, mAppRequestedModeByDisplay.get(displayId))) {
-                return;
-            }
-            final Vote baseModeRefreshRateVote;
-            final Vote sizeVote;
-            if (requestedMode != null) {
-                mAppRequestedModeByDisplay.put(displayId, requestedMode);
-                sizeVote = Vote.forSize(requestedMode.getPhysicalWidth(),
-                        requestedMode.getPhysicalHeight());
-                if (requestedMode.isSynthetic()) {
-                    // TODO: for synthetic mode we should not limit frame rate, we must ensure
-                    // that frame rate is reachable within other Votes constraints
-                    baseModeRefreshRateVote = Vote.forRenderFrameRates(
-                            requestedMode.getRefreshRate(), requestedMode.getRefreshRate());
-                } else {
-                    baseModeRefreshRateVote =
-                            Vote.forBaseModeRefreshRate(requestedMode.getRefreshRate());
-                }
-            } else {
-                mAppRequestedModeByDisplay.remove(displayId);
-                baseModeRefreshRateVote = null;
-                sizeVote = null;
-            }
-
-            mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
-                    baseModeRefreshRateVote);
-            mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
-        }
-
-        private void setAppPreferredRefreshRateRangeLocked(int displayId,
-                float requestedMinRefreshRateRange, float requestedMaxRefreshRateRange) {
-            final Vote vote;
-
-            RefreshRateRange refreshRateRange = null;
-            if (requestedMinRefreshRateRange > 0 || requestedMaxRefreshRateRange > 0) {
-                float min = requestedMinRefreshRateRange;
-                float max = requestedMaxRefreshRateRange > 0
-                        ? requestedMaxRefreshRateRange : Float.POSITIVE_INFINITY;
-                refreshRateRange = new RefreshRateRange(min, max);
-                if (refreshRateRange.min == 0 && refreshRateRange.max == 0) {
-                    // requestedMinRefreshRateRange/requestedMaxRefreshRateRange were invalid
-                    refreshRateRange = null;
-                }
-            }
-
-            if (Objects.equals(refreshRateRange,
-                    mAppPreferredRefreshRateRangeByDisplay.get(displayId))) {
-                return;
-            }
-
-            if (refreshRateRange != null) {
-                mAppPreferredRefreshRateRangeByDisplay.put(displayId, refreshRateRange);
-                vote = Vote.forRenderFrameRates(refreshRateRange.min, refreshRateRange.max);
-            } else {
-                mAppPreferredRefreshRateRangeByDisplay.remove(displayId);
-                vote = null;
-            }
-            mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE,
-                    vote);
-        }
-
         private Display.Mode findAppModeByIdLocked(int displayId, int modeId) {
             Display.Mode[] modes = mAppSupportedModesByDisplay.get(displayId);
             if (modes == null) {
@@ -1384,19 +1360,7 @@
 
         private void dumpLocked(PrintWriter pw) {
             pw.println("  AppRequestObserver");
-            pw.println("    mAppRequestedModeByDisplay:");
-            for (int i = 0; i < mAppRequestedModeByDisplay.size(); i++) {
-                final int id = mAppRequestedModeByDisplay.keyAt(i);
-                final Display.Mode mode = mAppRequestedModeByDisplay.valueAt(i);
-                pw.println("    " + id + " -> " + mode);
-            }
-            pw.println("    mAppPreferredRefreshRateRangeByDisplay:");
-            for (int i = 0; i < mAppPreferredRefreshRateRangeByDisplay.size(); i++) {
-                final int id = mAppPreferredRefreshRateRangeByDisplay.keyAt(i);
-                final RefreshRateRange refreshRateRange =
-                        mAppPreferredRefreshRateRangeByDisplay.valueAt(i);
-                pw.println("    " + id + " -> " + refreshRateRange);
-            }
+            pw.println("    mIgnorePreferredRefreshRate: " + mIgnorePreferredRefreshRate);
         }
     }
 
diff --git a/services/core/java/com/android/server/display/mode/RequestedRefreshRateVote.java b/services/core/java/com/android/server/display/mode/RequestedRefreshRateVote.java
new file mode 100644
index 0000000..843cf88
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/RequestedRefreshRateVote.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode;
+
+import android.annotation.NonNull;
+
+import java.util.Objects;
+
+class RequestedRefreshRateVote implements Vote {
+    final float mRefreshRate;
+
+    RequestedRefreshRateVote(float refreshRate) {
+        this.mRefreshRate = refreshRate;
+    }
+
+    @Override
+    public void updateSummary(@NonNull VoteSummary summary) {
+        summary.requestedRefreshRates.add(mRefreshRate);
+    }
+
+    @Override
+    public String toString() {
+        return "RequestedRefreshRateVote{ refreshRate=" + mRefreshRate + " }";
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof RequestedRefreshRateVote that)) return false;
+        return Float.compare(mRefreshRate, that.mRefreshRate) == 0;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mRefreshRate);
+    }
+}
diff --git a/services/core/java/com/android/server/display/mode/SyntheticModeManager.java b/services/core/java/com/android/server/display/mode/SyntheticModeManager.java
index 5b6bbc5..a83b939 100644
--- a/services/core/java/com/android/server/display/mode/SyntheticModeManager.java
+++ b/services/core/java/com/android/server/display/mode/SyntheticModeManager.java
@@ -32,7 +32,9 @@
  */
 public class SyntheticModeManager {
     private static final float FLOAT_TOLERANCE = 0.01f;
-    private static final float SYNTHETIC_MODE_HIGH_BOUNDARY = 60f + FLOAT_TOLERANCE;
+    private static final float SYNTHETIC_MODE_REFRESH_RATE = 60f;
+    private static final float SYNTHETIC_MODE_HIGH_BOUNDARY =
+            SYNTHETIC_MODE_REFRESH_RATE + FLOAT_TOLERANCE;
 
     private final boolean mSynthetic60HzModesEnabled;
 
@@ -62,7 +64,7 @@
                 nextModeId = mode.getModeId();
             }
 
-            float divisor = mode.getVsyncRate() / 60f;
+            float divisor = mode.getVsyncRate() / SYNTHETIC_MODE_REFRESH_RATE;
             boolean is60HzAchievable = Math.abs(divisor - Math.round(divisor)) < FLOAT_TOLERANCE;
             if (is60HzAchievable) {
                 sizes.put(new Size(mode.getPhysicalWidth(), mode.getPhysicalHeight()),
diff --git a/services/core/java/com/android/server/display/mode/Vote.java b/services/core/java/com/android/server/display/mode/Vote.java
index 3e8b3c5..1ec469c 100644
--- a/services/core/java/com/android/server/display/mode/Vote.java
+++ b/services/core/java/com/android/server/display/mode/Vote.java
@@ -186,6 +186,10 @@
         return new BaseModeRefreshRateVote(baseModeRefreshRate);
     }
 
+    static Vote forRequestedRefreshRate(float refreshRate) {
+        return new RequestedRefreshRateVote(refreshRate);
+    }
+
     static Vote forSupportedRefreshRates(List<SupportedModeData> supportedModes) {
         if (supportedModes.isEmpty()) {
             return null;
diff --git a/services/core/java/com/android/server/display/mode/VoteSummary.java b/services/core/java/com/android/server/display/mode/VoteSummary.java
index d4ce892..00a9226 100644
--- a/services/core/java/com/android/server/display/mode/VoteSummary.java
+++ b/services/core/java/com/android/server/display/mode/VoteSummary.java
@@ -23,7 +23,9 @@
 import android.view.SurfaceControl;
 
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 
 final class VoteSummary {
     private static final float FLOAT_TOLERANCE = SurfaceControl.RefreshRateRange.FLOAT_TOLERANCE;
@@ -38,7 +40,14 @@
     public int minWidth;
     public int minHeight;
     public boolean disableRefreshRateSwitching;
+    /**
+     *  available modes should have mode with specific refresh rate
+     */
     public float appRequestBaseModeRefreshRate;
+    /**
+     * requestRefreshRate should be within [minRenderFrameRate, maxRenderFrameRate] range
+     */
+    public Set<Float> requestedRefreshRates = new HashSet<>();
 
     @Nullable
     public List<SupportedRefreshRatesVote.RefreshRates> supportedRefreshRates;
@@ -329,6 +338,21 @@
             }
             return false;
         }
+
+        for (Float requestedRefreshRate : requestedRefreshRates) {
+            if (requestedRefreshRate < minRenderFrameRate
+                    || requestedRefreshRate > maxRenderFrameRate) {
+                if (mLoggingEnabled) {
+                    Slog.w(TAG, "Requested refreshRate is outside frame rate range"
+                            + ": requestedRefreshRates=" + requestedRefreshRates
+                            + ", requestedRefreshRate=" + requestedRefreshRate
+                            + ", minRenderFrameRate=" + minRenderFrameRate
+                            + ", maxRenderFrameRate=" + maxRenderFrameRate);
+                }
+                return false;
+            }
+        }
+
         return true;
     }
 
@@ -370,6 +394,7 @@
         minHeight = 0;
         disableRefreshRateSwitching = false;
         appRequestBaseModeRefreshRate = 0f;
+        requestedRefreshRates.clear();
         supportedRefreshRates = null;
         supportedModeIds = null;
         if (mLoggingEnabled) {
@@ -393,6 +418,7 @@
                 + ", minHeight=" + minHeight
                 + ", disableRefreshRateSwitching=" + disableRefreshRateSwitching
                 + ", appRequestBaseModeRefreshRate=" + appRequestBaseModeRefreshRate
+                + ", requestRefreshRates=" + requestedRefreshRates
                 + ", supportedRefreshRates=" + supportedRefreshRates
                 + ", supportedModeIds=" + supportedModeIds
                 + ", mIsDisplayResolutionRangeVotingEnabled="
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index d876a38..3c3bdd5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -269,8 +269,7 @@
         addValidationInfo(Constants.MESSAGE_REQUEST_SHORT_AUDIO_DESCRIPTOR,
                 oneByteValidator, ADDR_NOT_UNREGISTERED, ADDR_DIRECT);
         addValidationInfo(Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE,
-                new MinimumOneByteRangeValidator(0x00, 0x01),
-                ADDR_NOT_UNREGISTERED, ADDR_ALL);
+                new SingleByteRangeValidator(0x00, 0x01), ADDR_AUDIO_SYSTEM, ADDR_ALL);
         addValidationInfo(Constants.MESSAGE_SYSTEM_AUDIO_MODE_STATUS,
                 new SingleByteRangeValidator(0x00, 0x01), ADDR_NOT_UNREGISTERED,
                 ADDR_DIRECT);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index dbd1e65..6e027c6 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1029,6 +1029,10 @@
 
     /** Helper method for sending feature discovery command */
     private void reportFeatures(boolean isTvDeviceSetting) {
+        // <Report Features> should only be sent for HDMI 2.0
+        if (getCecVersion() < HdmiControlManager.HDMI_CEC_VERSION_2_0) {
+            return;
+        }
         // check if tv device is enabled for tv device specific RC profile setting
         if (isTvDeviceSetting) {
             if (isTvDeviceEnabled()) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiUtils.java b/services/core/java/com/android/server/hdmi/HdmiUtils.java
index 5646e1b..0688fbf 100644
--- a/services/core/java/com/android/server/hdmi/HdmiUtils.java
+++ b/services/core/java/com/android/server/hdmi/HdmiUtils.java
@@ -175,14 +175,15 @@
      *
      * @param logicalAddress the logical address to verify
      * @param deviceType the device type to check
-     * @throws IllegalArgumentException
      */
-    static void verifyAddressType(int logicalAddress, int deviceType) {
+    static boolean verifyAddressType(int logicalAddress, int deviceType) {
         List<Integer> actualDeviceTypes = getTypeFromAddress(logicalAddress);
         if (!actualDeviceTypes.contains(deviceType)) {
-            throw new IllegalArgumentException("Device type missmatch:[Expected:" + deviceType
-                    + ", Actual:" + actualDeviceTypes);
+            Slog.w(TAG,"Device type mismatch:[Expected:" + deviceType
+                    + ", Actual:" + actualDeviceTypes + "]");
+            return false;
         }
+        return true;
     }
 
     /**
diff --git a/services/core/java/com/android/server/hdmi/RequestArcAction.java b/services/core/java/com/android/server/hdmi/RequestArcAction.java
index 54c8c00..58e146e 100644
--- a/services/core/java/com/android/server/hdmi/RequestArcAction.java
+++ b/services/core/java/com/android/server/hdmi/RequestArcAction.java
@@ -19,6 +19,7 @@
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.IHdmiControlCallback;
+import android.util.Slog;
 
 /**
  * Base feature action class for &lt;Request ARC Initiation&gt;/&lt;Request ARC Termination&gt;.
@@ -38,13 +39,14 @@
      * @param source {@link HdmiCecLocalDevice} instance
      * @param avrAddress address of AV receiver. It should be AUDIO_SYSTEM type
      * @param callback callback to inform about the status of the action
-     * @throws IllegalArgumentException if device type of sourceAddress and avrAddress
-     *                      is invalid
      */
     RequestArcAction(HdmiCecLocalDevice source, int avrAddress, IHdmiControlCallback callback) {
         super(source, callback);
-        HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV);
-        HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+        if (!HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV) ||
+                !HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM)) {
+            Slog.w(TAG, "Device type mismatch, stop the action.");
+            finish();
+        }
         mAvrAddress = avrAddress;
     }
 
diff --git a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
index 32e274e..5ab22e1 100644
--- a/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
+++ b/services/core/java/com/android/server/hdmi/SetArcTransmissionStateAction.java
@@ -47,8 +47,11 @@
     SetArcTransmissionStateAction(HdmiCecLocalDevice source, int avrAddress,
             boolean enabled) {
         super(source);
-        HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV);
-        HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+        if (!HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV) ||
+                !HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM)) {
+            Slog.w(TAG, "Device type mismatch, stop the action.");
+            finish();
+        }
         mAvrAddress = avrAddress;
         mEnabled = enabled;
     }
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
index e96963b9..f14cda1 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
@@ -20,6 +20,7 @@
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.IHdmiControlCallback;
 import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.util.Slog;
 
 import java.util.List;
 
@@ -56,12 +57,14 @@
      * @param avrAddress logical address of AVR device
      * @param targetStatus Whether to enable the system audio mode or not
      * @param callback callback interface to be notified when it's done
-     * @throws IllegalArgumentException if device type of sourceAddress and avrAddress is invalid
      */
     SystemAudioAction(HdmiCecLocalDevice source, int avrAddress, boolean targetStatus,
             IHdmiControlCallback callback) {
         super(source, callback);
-        HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM);
+        if (!HdmiUtils.verifyAddressType(avrAddress, HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM)) {
+            Slog.w(TAG, "Device type mismatch, stop the action.");
+            finish();
+        }
         mAvrLogicalAddress = avrAddress;
         mTargetAudioStatus = targetStatus;
     }
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
index 99148c4..08a9387 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
@@ -19,12 +19,14 @@
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.IHdmiControlCallback;
+import android.util.Slog;
 
 /**
  * Feature action that handles System Audio initiated by AVR devices.
  */
 // Seq #33
 final class SystemAudioActionFromAvr extends SystemAudioAction {
+    private static final String TAG = "SystemAudioActionFromAvr";
     /**
      * Constructor
      *
@@ -32,12 +34,14 @@
      * @param avrAddress logical address of AVR device
      * @param targetStatus Whether to enable the system audio mode or not
      * @param callback callback interface to be notified when it's done
-     * @throws IllegalArgumentException if device type of tvAddress and avrAddress is invalid
      */
     SystemAudioActionFromAvr(HdmiCecLocalDevice source, int avrAddress,
             boolean targetStatus, IHdmiControlCallback callback) {
         super(source, avrAddress, targetStatus, callback);
-        HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV);
+        if (!HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV)) {
+            Slog.w(TAG, "Device type mismatch, stop the action.");
+            finish();
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
index 5c0c272..675aa31 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
@@ -18,13 +18,14 @@
 
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.IHdmiControlCallback;
+import android.util.Slog;
 
 
 /**
  * Feature action that handles System Audio initiated by TV devices.
  */
 final class SystemAudioActionFromTv extends SystemAudioAction {
-
+    private static final String TAG = "SystemAudioActionFromTv";
     /**
      * Constructor
      *
@@ -32,12 +33,14 @@
      * @param avrAddress logical address of AVR device
      * @param targetStatus Whether to enable the system audio mode or not
      * @param callback callback interface to be notified when it's done
-     * @throws IllegalArgumentException if device type of tvAddress is invalid
      */
     SystemAudioActionFromTv(HdmiCecLocalDevice sourceAddress, int avrAddress,
             boolean targetStatus, IHdmiControlCallback callback) {
         super(sourceAddress, avrAddress, targetStatus, callback);
-        HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV);
+        if (!HdmiUtils.verifyAddressType(getSourceAddress(), HdmiDeviceInfo.DEVICE_TV)) {
+            Slog.w(TAG, "Device type mismatch, stop the action.");
+            finish();
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeMapRepository.java b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeMapRepository.java
index dd6433d..82ecb4a 100644
--- a/services/core/java/com/android/server/inputmethod/AdditionalSubtypeMapRepository.java
+++ b/services/core/java/com/android/server/inputmethod/AdditionalSubtypeMapRepository.java
@@ -16,12 +16,16 @@
 
 package com.android.server.inputmethod;
 
+import android.annotation.AnyThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
+import android.annotation.WorkerThread;
 import android.content.Context;
 import android.content.pm.UserInfo;
 import android.os.Handler;
+import android.os.Process;
+import android.util.IntArray;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -29,6 +33,10 @@
 import com.android.server.LocalServices;
 import com.android.server.pm.UserManagerInternal;
 
+import java.util.ArrayList;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
 /**
  * Provides accesses to per-user additional {@link android.view.inputmethod.InputMethodSubtype}
  * persistent storages.
@@ -38,6 +46,152 @@
     @NonNull
     private static final SparseArray<AdditionalSubtypeMap> sPerUserMap = new SparseArray<>();
 
+    record WriteTask(@UserIdInt int userId, @NonNull AdditionalSubtypeMap subtypeMap,
+                     @NonNull InputMethodMap inputMethodMap) {
+    }
+
+    static final class SingleThreadedBackgroundWriter {
+        /**
+         * A {@link ReentrantLock} used to guard {@link #mPendingTasks} and {@link #mRemovedUsers}.
+         */
+        @NonNull
+        private final ReentrantLock mLock = new ReentrantLock();
+        /**
+         * A {@link Condition} associated with {@link #mLock} for producer to unblock consumer.
+         */
+        @NonNull
+        private final Condition mLockNotifier = mLock.newCondition();
+
+        @GuardedBy("mLock")
+        @NonNull
+        private final SparseArray<WriteTask> mPendingTasks = new SparseArray<>();
+
+        @GuardedBy("mLock")
+        private final IntArray mRemovedUsers = new IntArray();
+
+        @NonNull
+        private final Thread mWriterThread = new Thread("android.ime.as") {
+
+            /**
+             * Waits until the next data has come then return the result after filtering out any
+             * already removed users.
+             *
+             * @return A list of {@link WriteTask} to be written into persistent storage
+             */
+            @WorkerThread
+            private ArrayList<WriteTask> fetchNextTasks() {
+                final SparseArray<WriteTask> tasks;
+                final IntArray removedUsers;
+                mLock.lock();
+                try {
+                    while (true) {
+                        if (mPendingTasks.size() != 0) {
+                            tasks = mPendingTasks.clone();
+                            mPendingTasks.clear();
+                            if (mRemovedUsers.size() == 0) {
+                                removedUsers = null;
+                            } else {
+                                removedUsers = mRemovedUsers.clone();
+                            }
+                            break;
+                        }
+                        mLockNotifier.awaitUninterruptibly();
+                    }
+                } finally {
+                    mLock.unlock();
+                }
+                final int size = tasks.size();
+                final ArrayList<WriteTask> result = new ArrayList<>(size);
+                for (int i = 0; i < size; ++i) {
+                    final int userId = tasks.keyAt(i);
+                    if (removedUsers != null && removedUsers.contains(userId)) {
+                        continue;
+                    }
+                    result.add(tasks.valueAt(i));
+                }
+                return result;
+            }
+
+            @WorkerThread
+            @Override
+            public void run() {
+                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+
+                while (true) {
+                    final ArrayList<WriteTask> tasks = fetchNextTasks();
+                    tasks.forEach(task -> AdditionalSubtypeUtils.save(
+                            task.subtypeMap, task.inputMethodMap, task.userId));
+                }
+            }
+        };
+
+        /**
+         * Schedules a write operation
+         *
+         * @param userId the target user ID of this operation
+         * @param subtypeMap {@link AdditionalSubtypeMap} to be saved
+         * @param inputMethodMap {@link InputMethodMap} to be used to filter our {@code subtypeMap}
+         */
+        @AnyThread
+        void scheduleWriteTask(@UserIdInt int userId, @NonNull AdditionalSubtypeMap subtypeMap,
+                @NonNull InputMethodMap inputMethodMap) {
+            final var task = new WriteTask(userId, subtypeMap, inputMethodMap);
+            mLock.lock();
+            try {
+                if (mRemovedUsers.contains(userId)) {
+                    return;
+                }
+                mPendingTasks.put(userId, task);
+                mLockNotifier.signalAll();
+            } finally {
+                mLock.unlock();
+            }
+        }
+
+        /**
+         * Called back when a user is being created.
+         *
+         * @param userId The user ID to be created
+         */
+        @AnyThread
+        void onUserCreated(@UserIdInt int userId) {
+            mLock.lock();
+            try {
+                for (int i = mRemovedUsers.size() - 1; i >= 0; --i) {
+                    if (mRemovedUsers.get(i) == userId) {
+                        mRemovedUsers.remove(i);
+                    }
+                }
+            } finally {
+                mLock.unlock();
+            }
+        }
+
+        /**
+         * Called back when a user is being removed. Any pending task will be effectively canceled
+         * if the user is removed before the task is fulfilled.
+         *
+         * @param userId The user ID to be removed
+         */
+        @AnyThread
+        void onUserRemoved(@UserIdInt int userId) {
+            mLock.lock();
+            try {
+                mRemovedUsers.add(userId);
+                mPendingTasks.remove(userId);
+            } finally {
+                mLock.unlock();
+            }
+        }
+
+        void startThread() {
+            mWriterThread.start();
+        }
+    }
+
+    private static final SingleThreadedBackgroundWriter sWriter =
+            new SingleThreadedBackgroundWriter();
+
     /**
      * Not intended to be instantiated.
      */
@@ -64,9 +218,11 @@
             return;
         }
         sPerUserMap.put(userId, map);
-        // TODO: Offload this to a background thread.
-        // TODO: Skip if the previous data is exactly the same as new one.
-        AdditionalSubtypeUtils.save(map, inputMethodMap, userId);
+        sWriter.scheduleWriteTask(userId, map, inputMethodMap);
+    }
+
+    static void startWriterThread() {
+        sWriter.startThread();
     }
 
     static void initialize(@NonNull Handler handler, @NonNull Context context) {
@@ -78,6 +234,7 @@
                         @Override
                         public void onUserCreated(UserInfo user, @Nullable Object token) {
                             final int userId = user.id;
+                            sWriter.onUserCreated(userId);
                             handler.post(() -> {
                                 synchronized (ImfLock.class) {
                                     if (!sPerUserMap.contains(userId)) {
@@ -99,6 +256,7 @@
                         @Override
                         public void onUserRemoved(UserInfo user) {
                             final int userId = user.id;
+                            sWriter.onUserRemoved(userId);
                             handler.post(() -> {
                                 synchronized (ImfLock.class) {
                                     sPerUserMap.remove(userId);
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
index 79f1a9c..c19cb03 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
@@ -55,7 +55,6 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.OptionalInt;
-import java.util.function.IntConsumer;
 
 // TODO(b/210039666): See if we can make this class thread-safe.
 final class HandwritingModeController {
@@ -91,7 +90,6 @@
     private boolean mDelegationConnectionlessFlow;
     private Runnable mDelegationIdleTimeoutRunnable;
     private Handler mDelegationIdleTimeoutHandler;
-    private IntConsumer mPointerToolTypeConsumer;
     private final Runnable mDiscardDelegationTextRunnable;
     private HandwritingEventReceiverSurface mHandwritingSurface;
 
@@ -99,7 +97,7 @@
 
     @AnyThread
     HandwritingModeController(Context context, Looper uiThreadLooper,
-            Runnable inkWindowInitRunnable, IntConsumer toolTypeConsumer,
+            Runnable inkWindowInitRunnable,
             Runnable discardDelegationTextRunnable) {
         mContext = context;
         mLooper = uiThreadLooper;
@@ -109,7 +107,6 @@
         mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
         mCurrentRequestId = 0;
         mInkWindowInitRunnable = inkWindowInitRunnable;
-        mPointerToolTypeConsumer = toolTypeConsumer;
         mDiscardDelegationTextRunnable = discardDelegationTextRunnable;
     }
 
@@ -389,16 +386,10 @@
                     "Input Event should not be processed when IME has the spy channel.");
         }
 
-        if (!(ev instanceof MotionEvent)) {
+        if (!(ev instanceof MotionEvent event)) {
             Slog.wtf(TAG, "Received non-motion event in stylus monitor.");
             return false;
         }
-        final MotionEvent event = (MotionEvent) ev;
-        if (mPointerToolTypeConsumer != null && event.getAction() == MotionEvent.ACTION_DOWN) {
-            int toolType = event.getToolType(event.getActionIndex());
-            // notify IME of change in tool type.
-            mPointerToolTypeConsumer.accept(toolType);
-        }
         if (!event.isStylusPointer()) {
             return false;
         }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
index dace67f..f61ca61 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java
@@ -16,6 +16,8 @@
 
 package com.android.server.inputmethod;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -30,6 +32,9 @@
 import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.server.LocalServices;
 
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
 import java.util.Collections;
 import java.util.List;
 
@@ -38,6 +43,16 @@
  */
 public abstract class InputMethodManagerInternal {
     /**
+     * Indicates that the method is guaranteed to not require {@link ImfLock}.
+     *
+     * <p>You can call this method without worrying about system_server lock layering.</p>
+     */
+    @Retention(SOURCE)
+    @Target({ElementType.METHOD})
+    public @interface ImfLockFree {
+    }
+
+    /**
      * Listener for input method list changed events.
      */
     public interface InputMethodListListener {
@@ -53,6 +68,7 @@
      *
      * @param interactive the interactive mode parameter
      */
+    @ImfLockFree
     public abstract void setInteractive(boolean interactive);
 
     /**
@@ -61,6 +77,7 @@
      * @param reason               the reason for hiding the current input method
      * @param originatingDisplayId the display ID the request is originated
      */
+    @ImfLockFree
     public abstract void hideAllInputMethods(@SoftInputShowHideReason int reason,
             int originatingDisplayId);
 
@@ -143,6 +160,7 @@
      *
      * @param listener the listener to add
      */
+    @ImfLockFree
     public abstract void registerInputMethodListListener(InputMethodListListener listener);
 
     /**
@@ -178,6 +196,7 @@
      *
      * @param displayId the display hosting the IME window
      */
+    @ImfLockFree
     public abstract void removeImeSurface(int displayId);
 
     /**
@@ -188,12 +207,14 @@
      * @param disableImeIcon indicates whether IME icon should be enabled or not
      * @param displayId      the display for which to update the IME window status
      */
+    @ImfLockFree
     public abstract void updateImeWindowStatus(boolean disableImeIcon, int displayId);
 
     /**
      * Finish stylus handwriting by calling {@link InputMethodService#finishStylusHandwriting()} if
      * there is an ongoing handwriting session.
      */
+    @ImfLockFree
     public abstract void maybeFinishStylusHandwriting();
 
     /**
@@ -240,10 +261,12 @@
      */
     private static final InputMethodManagerInternal NOP =
             new InputMethodManagerInternal() {
+                @ImfLockFree
                 @Override
                 public void setInteractive(boolean interactive) {
                 }
 
+                @ImfLockFree
                 @Override
                 public void hideAllInputMethods(@SoftInputShowHideReason int reason,
                         int originatingDisplayId) {
@@ -282,6 +305,7 @@
                         int deviceId, @Nullable String imeId) {
                 }
 
+                @ImfLockFree
                 @Override
                 public void registerInputMethodListListener(InputMethodListListener listener) {
                 }
@@ -300,10 +324,12 @@
                 public void onImeParentChanged(int displayId) {
                 }
 
+                @ImfLockFree
                 @Override
                 public void removeImeSurface(int displayId) {
                 }
 
+                @ImfLockFree
                 @Override
                 public void updateImeWindowStatus(boolean disableImeIcon, int displayId) {
                 }
@@ -318,6 +344,7 @@
                         @UserIdInt int userId) {
                 }
 
+                @ImfLockFree
                 @Override
                 public void maybeFinishStylusHandwriting() {
                 }
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 976399d..d22438f 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -205,7 +205,6 @@
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
-import java.util.function.IntConsumer;
 import java.util.function.IntFunction;
 
 /**
@@ -1305,12 +1304,9 @@
                     com.android.internal.R.bool.config_preventImeStartupUnlessTextEditor);
             mNonPreemptibleInputMethods = mRes.getStringArray(
                     com.android.internal.R.array.config_nonPreemptibleInputMethods);
-            IntConsumer toolTypeConsumer =
-                    Flags.useHandwritingListenerForTooltype()
-                            ? toolType -> onUpdateEditorToolType(toolType) : null;
             Runnable discardDelegationTextRunnable = () -> discardHandwritingDelegationText();
             mHwController = new HandwritingModeController(mContext, thread.getLooper(),
-                    new InkWindowInitializer(), toolTypeConsumer, discardDelegationTextRunnable);
+                    new InkWindowInitializer(), discardDelegationTextRunnable);
             registerDeviceListenerAndCheckStylusSupport();
         }
     }
@@ -1547,6 +1543,10 @@
                 InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(
                         getPackageManagerForUser(mContext, currentUserId),
                         newSettings.getEnabledInputMethodList());
+
+                final var unused = SystemServerInitThreadPool.submit(
+                        AdditionalSubtypeMapRepository::startWriterThread,
+                        "Start AdditionalSubtypeMapRepository's writer thread");
             }
         }
     }
@@ -3412,8 +3412,10 @@
             ImeTracker.forLogging().onProgress(statsToken, ImeTracker.PHASE_SERVER_HAS_IME);
             mCurStatsToken = null;
 
-            if (lastClickToolType != MotionEvent.TOOL_TYPE_UNKNOWN) {
-                curMethod.updateEditorToolType(lastClickToolType);
+            if (Flags.useHandwritingListenerForTooltype()) {
+                maybeReportToolType();
+            } else if (lastClickToolType != MotionEvent.TOOL_TYPE_UNKNOWN) {
+                onUpdateEditorToolType(lastClickToolType);
             }
             mVisibilityApplier.performShowIme(windowToken, statsToken,
                     mVisibilityStateComputer.getShowFlagsForInputMethodServiceOnly(),
@@ -3427,6 +3429,29 @@
         return false;
     }
 
+    @GuardedBy("ImfLock.class")
+    private void maybeReportToolType() {
+        int lastDeviceId = mInputManagerInternal.getLastUsedInputDeviceId();
+        final InputManager im = mContext.getSystemService(InputManager.class);
+        if (im == null) {
+            return;
+        }
+        InputDevice device = im.getInputDevice(lastDeviceId);
+        if (device == null) {
+            return;
+        }
+        int toolType;
+        if (isStylusDevice(device)) {
+            toolType = MotionEvent.TOOL_TYPE_STYLUS;
+        } else if (isFingerDevice(device)) {
+            toolType = MotionEvent.TOOL_TYPE_FINGER;
+        } else {
+            // other toolTypes are irrelevant and reported as unknown.
+            toolType = MotionEvent.TOOL_TYPE_UNKNOWN;
+        }
+        onUpdateEditorToolType(toolType);
+    }
+
     @Override
     public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
             @NonNull ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
@@ -4098,17 +4123,17 @@
             if (additionalSubtypeMap != newAdditionalSubtypeMap) {
                 AdditionalSubtypeMapRepository.putAndSave(userId, newAdditionalSubtypeMap,
                         settings.getMethodMap());
-                final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext,
-                        userId, AdditionalSubtypeMapRepository.get(userId),
-                        DirectBootAwareness.AUTO);
-                InputMethodSettingsRepository.put(userId, newSettings);
-                if (isCurrentUser) {
-                    final long ident = Binder.clearCallingIdentity();
-                    try {
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    final InputMethodSettings newSettings = queryInputMethodServicesInternal(
+                            mContext, userId, AdditionalSubtypeMapRepository.get(userId),
+                            DirectBootAwareness.AUTO);
+                    InputMethodSettingsRepository.put(userId, newSettings);
+                    if (isCurrentUser) {
                         postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */);
-                    } finally {
-                        Binder.restoreCallingIdentity(ident);
                     }
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
                 }
             }
         }
@@ -4281,6 +4306,10 @@
                 || inputDevice.supportsSource(InputDevice.SOURCE_BLUETOOTH_STYLUS);
     }
 
+    private static boolean isFingerDevice(InputDevice inputDevice) {
+        return inputDevice.supportsSource(InputDevice.SOURCE_TOUCHSCREEN);
+    }
+
     @GuardedBy("ImfLock.class")
     private boolean hasSupportedStylusLocked() {
         return mStylusIds != null && mStylusIds.size() != 0;
@@ -4969,6 +4998,12 @@
         final int flags = PackageManager.GET_META_DATA
                 | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
                 | directBootAwarenessFlags;
+
+        // Beware that package visibility filtering will be enforced based on the effective calling
+        // identity (Binder.getCallingUid()), but our use case always expect Binder.getCallingUid()
+        // to return Process.SYSTEM_UID here. The actual filtering is implemented separately with
+        // canCallerAccessInputMethod().
+        // TODO(b/343108534): Use PackageManagerInternal#queryIntentServices() to pass SYSTEM_UID.
         final List<ResolveInfo> services = userAwareContext.getPackageManager().queryIntentServices(
                 new Intent(InputMethod.SERVICE_INTERFACE),
                 PackageManager.ResolveInfoFlags.of(flags));
@@ -5465,12 +5500,14 @@
 
     private final class LocalServiceImpl extends InputMethodManagerInternal {
 
+        @ImfLockFree
         @Override
         public void setInteractive(boolean interactive) {
             // Do everything in handler so as not to block the caller.
             mHandler.obtainMessage(MSG_SET_INTERACTIVE, interactive ? 1 : 0, 0).sendToTarget();
         }
 
+        @ImfLockFree
         @Override
         public void hideAllInputMethods(@SoftInputShowHideReason int reason,
                 int originatingDisplayId) {
@@ -5556,6 +5593,7 @@
             }
         }
 
+        @ImfLockFree
         @Override
         public void registerInputMethodListListener(InputMethodListListener listener) {
             mInputMethodListListeners.addIfAbsent(listener);
@@ -5603,11 +5641,13 @@
             }
         }
 
+        @ImfLockFree
         @Override
         public void removeImeSurface(int displayId) {
             mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE).sendToTarget();
         }
 
+        @ImfLockFree
         @Override
         public void updateImeWindowStatus(boolean disableImeIcon, int displayId) {
             mHandler.obtainMessage(MSG_UPDATE_IME_WINDOW_STATUS, disableImeIcon ? 1 : 0, 0)
@@ -5685,6 +5725,7 @@
             }
         }
 
+        @ImfLockFree
         @Override
         public void maybeFinishStylusHandwriting() {
             mHandler.removeMessages(MSG_FINISH_HANDWRITING);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
index 1c958a9..23f947c 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -367,9 +367,9 @@
         }
 
         protected void dump(final Printer pw, final String prefix) {
-            for (int i = 0; i < mUsageHistoryOfSubtypeListItemIndex.length; ++i) {
-                final int rank = mUsageHistoryOfSubtypeListItemIndex[i];
-                final ImeSubtypeListItem item = mImeSubtypeList.get(i);
+            for (int rank = 0; rank < mUsageHistoryOfSubtypeListItemIndex.length; ++rank) {
+                final int index = mUsageHistoryOfSubtypeListItemIndex[rank];
+                final ImeSubtypeListItem item = mImeSubtypeList.get(index);
                 pw.println(prefix + "rank=" + rank + " item=" + item);
             }
         }
diff --git a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
index d932bd4..b9e0960 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
@@ -84,9 +84,16 @@
      * from the delegate selector.
      */
     private static final String LOCALES_FROM_DELEGATE_PREFS = "LocalesFromDelegatePrefs.xml";
+    private static final String LOCALES_STAGED_DATA_PREFS = "LocalesStagedDataPrefs.xml";
+    private static final String ARCHIVED_PACKAGES_PREFS = "ArchivedPackagesPrefs.xml";
     // Stage data would be deleted on reboot since it's stored in memory. So it's retained until
     // retention period OR next reboot, whichever happens earlier.
     private static final Duration STAGE_DATA_RETENTION_PERIOD = Duration.ofDays(3);
+    // Store the locales staged data for the specified package in the SharedPreferences. The format
+    // is locales s:setFromDelegate
+    // For example: en-US s:true
+    private static final String STRING_SPLIT = " s:";
+    private static final String KEY_STAGED_DATA_TIME = "staged_data_time";
 
     private final LocaleManagerService mLocaleManagerService;
     private final PackageManager mPackageManager;
@@ -94,39 +101,34 @@
     private final Context mContext;
     private final Object mStagedDataLock = new Object();
 
-    // Staged data map keyed by user-id to handle multi-user scenario / work profiles. We are using
-    // SparseArray because it is more memory-efficient than a HashMap.
-    private final SparseArray<StagedData> mStagedData;
-
     // SharedPreferences to store packages whose app-locale was set by a delegate, as opposed to
     // the application setting the app-locale itself.
     private final SharedPreferences mDelegateAppLocalePackages;
+    // For unit tests
+    private final SparseArray<File> mStagedDataFiles;
+    private final File mArchivedPackagesFile;
+
     private final BroadcastReceiver mUserMonitor;
-    // To determine whether an app is pre-archived, check for Intent.EXTRA_ARCHIVAL upon receiving
-    // the initial PACKAGE_ADDED broadcast. If it is indeed pre-archived, perform the data
-    // restoration during the second PACKAGE_ADDED broadcast, which is sent subsequently when the
-    // app is installed.
-    private final Set<String> mPkgsToRestore;
 
     LocaleManagerBackupHelper(LocaleManagerService localeManagerService,
             PackageManager packageManager, HandlerThread broadcastHandlerThread) {
         this(localeManagerService.mContext, localeManagerService, packageManager, Clock.systemUTC(),
-                new SparseArray<>(), broadcastHandlerThread, null);
+                broadcastHandlerThread, null, null, null);
     }
 
-    @VisibleForTesting LocaleManagerBackupHelper(Context context,
-            LocaleManagerService localeManagerService,
-            PackageManager packageManager, Clock clock, SparseArray<StagedData> stagedData,
-            HandlerThread broadcastHandlerThread, SharedPreferences delegateAppLocalePackages) {
+    @VisibleForTesting
+    LocaleManagerBackupHelper(Context context, LocaleManagerService localeManagerService,
+            PackageManager packageManager, Clock clock, HandlerThread broadcastHandlerThread,
+            SparseArray<File> stagedDataFiles, File archivedPackagesFile,
+            SharedPreferences delegateAppLocalePackages) {
         mContext = context;
         mLocaleManagerService = localeManagerService;
         mPackageManager = packageManager;
         mClock = clock;
-        mStagedData = stagedData;
         mDelegateAppLocalePackages = delegateAppLocalePackages != null ? delegateAppLocalePackages
-                : createPersistedInfo();
-        mPkgsToRestore = new ArraySet<>();
-
+            : createPersistedInfo();
+        mArchivedPackagesFile = archivedPackagesFile;
+        mStagedDataFiles = stagedDataFiles;
         mUserMonitor = new UserMonitor();
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_USER_REMOVED);
@@ -148,7 +150,7 @@
         }
 
         synchronized (mStagedDataLock) {
-            cleanStagedDataForOldEntriesLocked();
+            cleanStagedDataForOldEntriesLocked(userId);
         }
 
         HashMap<String, LocalesInfo> pkgStates = new HashMap<>();
@@ -207,14 +209,11 @@
         return out.toByteArray();
     }
 
-    private void cleanStagedDataForOldEntriesLocked() {
-        for (int i = 0; i < mStagedData.size(); i++) {
-            int userId = mStagedData.keyAt(i);
-            StagedData stagedData = mStagedData.get(userId);
-            if (stagedData.mCreationTimeMillis
-                    < mClock.millis() - STAGE_DATA_RETENTION_PERIOD.toMillis()) {
-                deleteStagedDataLocked(userId);
-            }
+    private void cleanStagedDataForOldEntriesLocked(@UserIdInt int userId) {
+        Long created_time = getStagedDataSp(userId).getLong(KEY_STAGED_DATA_TIME, -1);
+        if (created_time != -1
+                && created_time < mClock.millis() - STAGE_DATA_RETENTION_PERIOD.toMillis()) {
+            deleteStagedDataLocked(userId);
         }
     }
 
@@ -252,20 +251,16 @@
         // performed simultaneously.
         synchronized (mStagedDataLock) {
             // Backups for apps which are yet to be installed.
-            StagedData stagedData = new StagedData(mClock.millis(), new HashMap<>());
-
             for (String pkgName : pkgStates.keySet()) {
                 LocalesInfo localesInfo = pkgStates.get(pkgName);
                 // Check if the application is already installed for the concerned user.
                 if (isPackageInstalledForUser(pkgName, userId)) {
-                    if (mPkgsToRestore != null) {
-                        mPkgsToRestore.remove(pkgName);
-                    }
+                    removeFromArchivedPackagesInfo(userId, pkgName);
                     // Don't apply the restore if the locales have already been set for the app.
                     checkExistingLocalesAndApplyRestore(pkgName, localesInfo, userId);
                 } else {
                     // Stage the data if the app isn't installed.
-                    stagedData.mPackageStates.put(pkgName, localesInfo);
+                    storeStagedDataInfo(userId, pkgName, localesInfo);
                     if (DEBUG) {
                         Slog.d(TAG, "Add locales=" + localesInfo.mLocales
                                 + " fromDelegate=" + localesInfo.mSetFromDelegate
@@ -274,8 +269,9 @@
                 }
             }
 
-            if (!stagedData.mPackageStates.isEmpty()) {
-                mStagedData.put(userId, stagedData);
+            // Create the time if the data is being staged.
+            if (!getStagedDataSp(userId).getAll().isEmpty()) {
+                storeStagedDataCreatedTime(userId);
             }
         }
     }
@@ -293,14 +289,23 @@
      * added on device.
      */
     void onPackageAddedWithExtras(String packageName, int uid, Bundle extras) {
-        boolean archived = false;
+        int userId = UserHandle.getUserId(uid);
         if (extras != null) {
-            archived = extras.getBoolean(Intent.EXTRA_ARCHIVAL, false);
-            if (archived && mPkgsToRestore != null) {
-                mPkgsToRestore.add(packageName);
+            // To determine whether an app is pre-archived, check for Intent.EXTRA_ARCHIVAL upon
+            // receiving the initial PACKAGE_ADDED broadcast. If it is indeed pre-archived, perform
+            // the data restoration during the second PACKAGE_ADDED broadcast, which is sent
+            // subsequently when the app is installed.
+            boolean archived = extras.getBoolean(Intent.EXTRA_ARCHIVAL, false);
+            if (DEBUG) {
+                Slog.d(TAG,
+                        "onPackageAddedWithExtras packageName: " + packageName + ", userId: "
+                                + userId + ", archived: " + archived);
+            }
+            if (archived) {
+                addInArchivedPackagesInfo(userId, packageName);
             }
         }
-        checkStageDataAndApplyRestore(packageName, uid);
+        checkStageDataAndApplyRestore(packageName, userId);
     }
 
     /**
@@ -310,9 +315,32 @@
      */
     void onPackageUpdateFinished(String packageName, int uid) {
         int userId = UserHandle.getUserId(uid);
-        if (mPkgsToRestore != null && mPkgsToRestore.contains(packageName)) {
-            mPkgsToRestore.remove(packageName);
-            checkStageDataAndApplyRestore(packageName, uid);
+        if (DEBUG) {
+            Slog.d(TAG,
+                    "onPackageUpdateFinished userId: " + userId + ", packageName: " + packageName);
+        }
+        String user = Integer.toString(userId);
+        File file = getArchivedPackagesFile();
+        if (file.exists()) {
+            SharedPreferences sp = getArchivedPackagesSp(file);
+            Set<String> packageNames = new ArraySet<>(sp.getStringSet(user, new ArraySet<>()));
+            if (packageNames.remove(packageName)) {
+                SharedPreferences.Editor editor = sp.edit();
+                if (packageNames.isEmpty()) {
+                    if (!editor.remove(user).commit()) {
+                        Slog.e(TAG, "Failed to remove the user");
+                    }
+                    if (sp.getAll().isEmpty()) {
+                        file.delete();
+                    }
+                } else {
+                    // commit and log the result.
+                    if (!editor.putStringSet(user, packageNames).commit()) {
+                        Slog.e(TAG, "failed to remove the package");
+                    }
+                }
+                checkStageDataAndApplyRestore(packageName, userId);
+            }
         }
         cleanApplicationLocalesIfNeeded(packageName, userId);
     }
@@ -347,16 +375,16 @@
         }
     }
 
-    private void checkStageDataAndApplyRestore(String packageName, int uid) {
+    private void checkStageDataAndApplyRestore(String packageName, int userId) {
         try {
             synchronized (mStagedDataLock) {
-                cleanStagedDataForOldEntriesLocked();
-
-                int userId = UserHandle.getUserId(uid);
-                if (mStagedData.contains(userId)) {
-                    if (mPkgsToRestore != null) {
-                        mPkgsToRestore.remove(packageName);
+                cleanStagedDataForOldEntriesLocked(userId);
+                if (!getStagedDataSp(userId).getString(packageName, "").isEmpty()) {
+                    if (DEBUG) {
+                        Slog.d(TAG,
+                                "checkStageDataAndApplyRestore, remove package and restore data");
                     }
+                    removeFromArchivedPackagesInfo(userId, packageName);
                     // Perform lazy restore only if the staged data exists.
                     doLazyRestoreLocked(packageName, userId);
                 }
@@ -417,8 +445,17 @@
         }
     }
 
-    private void deleteStagedDataLocked(@UserIdInt int userId) {
-        mStagedData.remove(userId);
+    void deleteStagedDataLocked(@UserIdInt int userId) {
+        File stagedFile = getStagedDataFile(userId);
+        SharedPreferences sp = getStagedDataSp(stagedFile);
+        // commit and log the result.
+        if (!sp.edit().clear().commit()) {
+            Slog.e(TAG, "Failed to commit data!");
+        }
+
+        if (stagedFile.exists()) {
+            stagedFile.delete();
+        }
     }
 
     /**
@@ -434,7 +471,7 @@
                         ATTR_PACKAGE_NAME);
                 String languageTags = parser.getAttributeValue(/* namespace= */ null, ATTR_LOCALES);
                 boolean delegateSelector = parser.getAttributeBoolean(/* namespace= */ null,
-                        ATTR_DELEGATE_SELECTOR);
+                        ATTR_DELEGATE_SELECTOR, false);
 
                 if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(languageTags)) {
                     LocalesInfo localesInfo = new LocalesInfo(languageTags, delegateSelector);
@@ -473,16 +510,6 @@
         out.endDocument();
     }
 
-    static class StagedData {
-        final long mCreationTimeMillis;
-        final HashMap<String, LocalesInfo> mPackageStates;
-
-        StagedData(long creationTimeMillis, HashMap<String, LocalesInfo> pkgStates) {
-            mCreationTimeMillis = creationTimeMillis;
-            mPackageStates = pkgStates;
-        }
-    }
-
     static class LocalesInfo {
         final String mLocales;
         final boolean mSetFromDelegate;
@@ -508,6 +535,7 @@
                     synchronized (mStagedDataLock) {
                         deleteStagedDataLocked(userId);
                         removeProfileFromPersistedInfo(userId);
+                        removeArchivedPackagesForUser(userId);
                     }
                 }
             } catch (Exception e) {
@@ -533,26 +561,159 @@
             return;
         }
 
-        StagedData stagedData = mStagedData.get(userId);
-        for (String pkgName : stagedData.mPackageStates.keySet()) {
-            LocalesInfo localesInfo = stagedData.mPackageStates.get(pkgName);
-
-            if (pkgName.equals(packageName)) {
-
-                checkExistingLocalesAndApplyRestore(pkgName, localesInfo, userId);
-
-                // Remove the restored entry from the staged data list.
-                stagedData.mPackageStates.remove(pkgName);
-
-                // Remove the stage data entry for user if there are no more packages to restore.
-                if (stagedData.mPackageStates.isEmpty()) {
-                    mStagedData.remove(userId);
-                }
-
-                // No need to loop further after restoring locales because the staged data will
-                // contain at most one entry for the newly added package.
-                break;
+        SharedPreferences sp = getStagedDataSp(userId);
+        String value = sp.getString(packageName, "");
+        if (!value.isEmpty()) {
+            String[] info = value.split(STRING_SPLIT);
+            if (info == null || info.length != 2) {
+                Slog.e(TAG, "Failed to restore data");
+                return;
             }
+            LocalesInfo localesInfo = new LocalesInfo(info[0], Boolean.parseBoolean(info[1]));
+            checkExistingLocalesAndApplyRestore(packageName, localesInfo, userId);
+
+            // Remove the restored entry from the staged data list.
+            if (!sp.edit().remove(packageName).commit()) {
+                Slog.e(TAG, "Failed to commit data!");
+            }
+        }
+
+        // Remove the stage data entry for user if there are no more packages to restore.
+        if (sp.getAll().size() == 1 && sp.getLong(KEY_STAGED_DATA_TIME, -1) != -1) {
+            deleteStagedDataLocked(userId);
+        }
+    }
+
+    private File getStagedDataFile(@UserIdInt int userId) {
+        return mStagedDataFiles == null ? new File(Environment.getDataSystemDeDirectory(userId),
+            LOCALES_STAGED_DATA_PREFS) : mStagedDataFiles.get(userId);
+    }
+
+    private SharedPreferences getStagedDataSp(File file) {
+        return mStagedDataFiles == null ? mContext.createDeviceProtectedStorageContext()
+            .getSharedPreferences(file, Context.MODE_PRIVATE)
+            : mContext.getSharedPreferences(file, Context.MODE_PRIVATE);
+    }
+
+    private SharedPreferences getStagedDataSp(@UserIdInt int userId) {
+        return mStagedDataFiles == null ? mContext.createDeviceProtectedStorageContext()
+            .getSharedPreferences(getStagedDataFile(userId), Context.MODE_PRIVATE)
+            : mContext.getSharedPreferences(mStagedDataFiles.get(userId), Context.MODE_PRIVATE);
+    }
+
+    /**
+     * Store the staged locales info.
+     */
+    private void storeStagedDataInfo(@UserIdInt int userId, @NonNull String packageName,
+            @NonNull LocalesInfo localesInfo) {
+        if (DEBUG) {
+            Slog.d(TAG, "storeStagedDataInfo, userId: " + userId + ", packageName: " + packageName
+                    + ", localesInfo.mLocales: " + localesInfo.mLocales
+                    + ", localesInfo.mSetFromDelegate: " + localesInfo.mSetFromDelegate);
+        }
+        String info =
+                localesInfo.mLocales + STRING_SPLIT + String.valueOf(localesInfo.mSetFromDelegate);
+        SharedPreferences sp = getStagedDataSp(userId);
+        // commit and log the result.
+        if (!sp.edit().putString(packageName, info).commit()) {
+            Slog.e(TAG, "Failed to commit data!");
+        }
+    }
+
+    /**
+     * Store the time of creation for staged locales info.
+     */
+    private void storeStagedDataCreatedTime(@UserIdInt int userId) {
+        SharedPreferences sp = getStagedDataSp(userId);
+        // commit and log the result.
+        if (!sp.edit().putLong(KEY_STAGED_DATA_TIME, mClock.millis()).commit()) {
+            Slog.e(TAG, "Failed to commit data!");
+        }
+    }
+
+    private File getArchivedPackagesFile() {
+        return mArchivedPackagesFile == null ? new File(
+            Environment.getDataSystemDeDirectory(UserHandle.USER_SYSTEM),
+            ARCHIVED_PACKAGES_PREFS) : mArchivedPackagesFile;
+    }
+
+    private SharedPreferences getArchivedPackagesSp(File file) {
+        return mArchivedPackagesFile == null ? mContext.createDeviceProtectedStorageContext()
+            .getSharedPreferences(file, Context.MODE_PRIVATE)
+            : mContext.getSharedPreferences(file, Context.MODE_PRIVATE);
+    }
+
+    /**
+     * Add the package into the archived packages list.
+     */
+    private void addInArchivedPackagesInfo(@UserIdInt int userId, @NonNull String packageName) {
+        String user = Integer.toString(userId);
+        SharedPreferences sp = getArchivedPackagesSp(getArchivedPackagesFile());
+        Set<String> packageNames = new ArraySet<>(sp.getStringSet(user, new ArraySet<>()));
+        if (DEBUG) {
+            Slog.d(TAG, "addInArchivedPackagesInfo before packageNames: " + packageNames
+                    + ", packageName: " + packageName);
+        }
+        if (packageNames.add(packageName)) {
+            // commit and log the result.
+            if (!sp.edit().putStringSet(user, packageNames).commit()) {
+                Slog.e(TAG, "failed to add the package");
+            }
+        }
+    }
+
+    /**
+     * Remove the package from the archived packages list.
+     */
+    private void removeFromArchivedPackagesInfo(@UserIdInt int userId,
+            @NonNull String packageName) {
+        File file = getArchivedPackagesFile();
+        if (file.exists()) {
+            String user = Integer.toString(userId);
+            SharedPreferences sp = getArchivedPackagesSp(getArchivedPackagesFile());
+            Set<String> packageNames = new ArraySet<>(sp.getStringSet(user, new ArraySet<>()));
+            if (DEBUG) {
+                Slog.d(TAG, "removeFromArchivedPackagesInfo before packageNames: " + packageNames
+                        + ", packageName: " + packageName);
+            }
+            if (packageNames.remove(packageName)) {
+                SharedPreferences.Editor editor = sp.edit();
+                if (packageNames.isEmpty()) {
+                    if (!editor.remove(user).commit()) {
+                        Slog.e(TAG, "Failed to remove user");
+                    }
+                    if (sp.getAll().isEmpty()) {
+                        file.delete();
+                    }
+                } else {
+                    // commit and log the result.
+                    if (!editor.putStringSet(user, packageNames).commit()) {
+                        Slog.e(TAG, "failed to remove the package");
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Remove the user from the archived packages list.
+     */
+    private void removeArchivedPackagesForUser(@UserIdInt int userId) {
+        String user = Integer.toString(userId);
+        File file = getArchivedPackagesFile();
+        SharedPreferences sp = getArchivedPackagesSp(file);
+
+        if (sp == null || !sp.contains(user)) {
+            Slog.w(TAG, "The profile is not existed in the archived package info");
+            return;
+        }
+
+        if (!sp.edit().remove(user).commit()) {
+            Slog.e(TAG, "Failed to remove user");
+        }
+
+        if (sp.getAll().isEmpty() && file.exists()) {
+            file.delete();
         }
     }
 
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index b14702d..ae3d36a 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -593,7 +593,7 @@
         public RebootEscrowManager getRebootEscrowManager(RebootEscrowManager.Callbacks callbacks,
                 LockSettingsStorage storage) {
             return new RebootEscrowManager(mContext, callbacks, storage,
-                    getHandler(getServiceThread()));
+                    getHandler(getServiceThread()), getUserManagerInternal());
         }
 
         public int binderGetCallingUid() {
@@ -1243,23 +1243,24 @@
         }
     }
 
-    private void enforceFrpResolved() {
+    private void enforceFrpNotActive() {
         final int mainUserId = mInjector.getUserManagerInternal().getMainUserId();
         if (mainUserId < 0) {
-            Slog.d(TAG, "No Main user on device; skipping enforceFrpResolved");
+            Slog.d(TAG, "No Main user on device; skipping enforceFrpNotActive");
             return;
         }
-        final ContentResolver cr = mContext.getContentResolver();
 
+        final ContentResolver cr = mContext.getContentResolver();
         final boolean inSetupWizard = Settings.Secure.getIntForUser(cr,
                 Settings.Secure.USER_SETUP_COMPLETE, 0, mainUserId) == 0;
-        final boolean secureFrp = android.security.Flags.frpEnforcement()
+        final boolean isFrpActive = android.security.Flags.frpEnforcement()
                 ? mStorage.isFactoryResetProtectionActive()
-                : (Settings.Global.getInt(cr, Settings.Global.SECURE_FRP_MODE, 0) == 1);
+                : (Settings.Global.getInt(cr, Settings.Global.SECURE_FRP_MODE, 0) == 1)
+                        && inSetupWizard;
 
-        if (inSetupWizard && secureFrp) {
-            throw new SecurityException("Cannot change credential in SUW while factory reset"
-                    + " protection is not resolved yet");
+        if (isFrpActive) {
+            throw new SecurityException("Cannot change credential while factory reset protection"
+                    + " is active");
         }
     }
 
@@ -1831,7 +1832,7 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            enforceFrpResolved();
+            enforceFrpNotActive();
             // When changing credential for profiles with unified challenge, some callers
             // will pass in empty credential while others will pass in the credential of
             // the parent user. setLockCredentialInternal() handles the formal case (empty
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index e1cd2c5..d0b8990 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -51,16 +51,20 @@
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.widget.RebootEscrowListener;
+import com.android.server.pm.UserManagerInternal;
 
 import java.io.IOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Objects;
+import java.util.Set;
 
 import javax.crypto.SecretKey;
 
@@ -138,6 +142,7 @@
             ERROR_KEYSTORE_FAILURE,
             ERROR_NO_NETWORK,
             ERROR_TIMEOUT_EXHAUSTED,
+            ERROR_NO_REBOOT_ESCROW_DATA,
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface RebootEscrowErrorCode {
@@ -153,6 +158,7 @@
     static final int ERROR_KEYSTORE_FAILURE = 7;
     static final int ERROR_NO_NETWORK = 8;
     static final int ERROR_TIMEOUT_EXHAUSTED = 9;
+    static final int ERROR_NO_REBOOT_ESCROW_DATA = 10;
 
     private @RebootEscrowErrorCode int mLoadEscrowDataErrorCode = ERROR_NONE;
 
@@ -222,11 +228,16 @@
         private final RebootEscrowKeyStoreManager mKeyStoreManager;
         private final LockSettingsStorage mStorage;
         private RebootEscrowProviderInterface mRebootEscrowProvider;
+        private final UserManagerInternal mUserManagerInternal;
 
-        Injector(Context context, LockSettingsStorage storage) {
+        Injector(
+                Context context,
+                LockSettingsStorage storage,
+                UserManagerInternal userManagerInternal) {
             mContext = context;
             mStorage = storage;
             mKeyStoreManager = new RebootEscrowKeyStoreManager();
+            mUserManagerInternal = userManagerInternal;
         }
 
         private RebootEscrowProviderInterface createRebootEscrowProvider() {
@@ -326,6 +337,10 @@
             return (UserManager) mContext.getSystemService(Context.USER_SERVICE);
         }
 
+        public UserManagerInternal getUserManagerInternal() {
+            return mUserManagerInternal;
+        }
+
         public RebootEscrowKeyStoreManager getKeyStoreManager() {
             return mKeyStoreManager;
         }
@@ -402,8 +417,8 @@
     }
 
     RebootEscrowManager(Context context, Callbacks callbacks, LockSettingsStorage storage,
-            Handler handler) {
-        this(new Injector(context, storage), callbacks, storage, handler);
+                        Handler handler, UserManagerInternal userManagerInternal) {
+        this(new Injector(context, storage, userManagerInternal), callbacks, storage, handler);
     }
 
     @VisibleForTesting
@@ -451,18 +466,50 @@
         onEscrowRestoreComplete(false, attemptCount, retryHandler);
     }
 
-    void loadRebootEscrowDataIfAvailable(Handler retryHandler) {
-        List<UserInfo> users = mUserManager.getUsers();
-        List<UserInfo> rebootEscrowUsers = new ArrayList<>();
+    private List<UserInfo> getUsersToUnlock(List<UserInfo> users) {
+        // System user must be unlocked to unlock any other user
+        if (mCallbacks.isUserSecure(USER_SYSTEM) && !mStorage.hasRebootEscrow(USER_SYSTEM)) {
+            Slog.i(TAG, "No reboot escrow data found for system user");
+            return Collections.emptyList();
+        }
+
+        Set<Integer> noEscrowDataUsers = new HashSet<>();
         for (UserInfo user : users) {
-            if (mCallbacks.isUserSecure(user.id) && mStorage.hasRebootEscrow(user.id)) {
-                rebootEscrowUsers.add(user);
+            if (mCallbacks.isUserSecure(user.id)
+                    && !mStorage.hasRebootEscrow(user.id)) {
+                Slog.d(TAG, "No reboot escrow data found for user " + user);
+                noEscrowDataUsers.add(user.id);
             }
         }
 
+        List<UserInfo> rebootEscrowUsers = new ArrayList<>();
+        for (UserInfo user : users) {
+            // No lskf, no need to unlock.
+            if (!mCallbacks.isUserSecure(user.id)) {
+                continue;
+            }
+            // Don't unlock if user or user's parent does not have reboot data
+            int userId = user.id;
+            if (noEscrowDataUsers.contains(userId)
+                    || noEscrowDataUsers.contains(
+                            mInjector.getUserManagerInternal().getProfileParentId(userId))) {
+                continue;
+            }
+            rebootEscrowUsers.add(user);
+        }
+        return rebootEscrowUsers;
+    }
+
+    void loadRebootEscrowDataIfAvailable(Handler retryHandler) {
+        List<UserInfo> users = mUserManager.getUsers();
+        List<UserInfo> rebootEscrowUsers = getUsersToUnlock(users);
+
         if (rebootEscrowUsers.isEmpty()) {
             Slog.i(TAG, "No reboot escrow data found for users,"
                     + " skipping loading escrow data");
+            setLoadEscrowDataErrorCode(ERROR_NO_REBOOT_ESCROW_DATA, retryHandler);
+            reportMetricOnRestoreComplete(
+                    /* success= */ false, /* attemptCount= */ 1, retryHandler);
             clearMetricsStorage();
             return;
         }
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 1bc2a5e..09605fe 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -21,6 +21,7 @@
 import android.content.ComponentName;
 import android.media.MediaRoute2Info;
 import android.media.MediaRoute2ProviderInfo;
+import android.media.MediaRouter2;
 import android.media.RouteDiscoveryPreference;
 import android.media.RoutingSessionInfo;
 import android.os.Bundle;
@@ -58,7 +59,7 @@
     public abstract void requestCreateSession(
             long requestId,
             String packageName,
-            String routeId,
+            String routeOriginalId,
             @Nullable Bundle sessionHints,
             @RoutingSessionInfo.TransferReason int transferReason,
             @NonNull UserHandle transferInitiatorUserHandle,
@@ -76,13 +77,15 @@
             long requestId,
             @NonNull UserHandle transferInitiatorUserHandle,
             @NonNull String transferInitiatorPackageName,
-            String sessionId,
-            String routeId,
+            String sessionOriginalId,
+            String routeOriginalId,
             @RoutingSessionInfo.TransferReason int transferReason);
 
-    public abstract void setRouteVolume(long requestId, String routeId, int volume);
-    public abstract void setSessionVolume(long requestId, String sessionId, int volume);
-    public abstract void prepareReleaseSession(@NonNull String sessionId);
+    public abstract void setRouteVolume(long requestId, String routeOriginalId, int volume);
+
+    public abstract void setSessionVolume(long requestId, String sessionOriginalId, int volume);
+
+    public abstract void prepareReleaseSession(@NonNull String sessionUniqueId);
 
     @NonNull
     public String getUniqueId() {
@@ -172,4 +175,59 @@
                 @NonNull RoutingSessionInfo sessionInfo);
         void onRequestFailed(@NonNull MediaRoute2Provider provider, long requestId, int reason);
     }
+
+    /**
+     * Holds session creation or transfer initiation information for a transfer in flight.
+     *
+     * <p>The initiator app is typically also the {@link RoutingSessionInfo#getClientPackageName()
+     * client app}, with the exception of the {@link MediaRouter2#getSystemController() system
+     * routing session} which is exceptional in that it's shared among all apps.
+     *
+     * <p>For the system routing session, the initiator app is the one that programmatically
+     * triggered the transfer (for example, via {@link MediaRouter2#transferTo}), or the target app
+     * of the proxy router that did the transfer.
+     *
+     * @see MediaRouter2.RoutingController#wasTransferInitiatedBySelf()
+     * @see RoutingSessionInfo#getTransferInitiatorPackageName()
+     * @see RoutingSessionInfo#getTransferInitiatorUserHandle()
+     */
+    protected static class SessionCreationOrTransferRequest {
+
+        /**
+         * The id of the request, or {@link
+         * android.media.MediaRoute2ProviderService#REQUEST_ID_NONE} if unknown.
+         */
+        public final long mRequestId;
+
+        /** The {@link MediaRoute2Info#getOriginalId()} original id} of the target route. */
+        @NonNull public final String mTargetOriginalRouteId;
+
+        @RoutingSessionInfo.TransferReason public final int mTransferReason;
+
+        /** The {@link android.os.UserHandle} on which the initiator app is running. */
+        @NonNull public final UserHandle mTransferInitiatorUserHandle;
+
+        @NonNull public final String mTransferInitiatorPackageName;
+
+        SessionCreationOrTransferRequest(
+                long requestId,
+                @NonNull String targetOriginalRouteId,
+                @RoutingSessionInfo.TransferReason int transferReason,
+                @NonNull UserHandle transferInitiatorUserHandle,
+                @NonNull String transferInitiatorPackageName) {
+            mRequestId = requestId;
+            mTargetOriginalRouteId = targetOriginalRouteId;
+            mTransferReason = transferReason;
+            mTransferInitiatorUserHandle = transferInitiatorUserHandle;
+            mTransferInitiatorPackageName = transferInitiatorPackageName;
+        }
+
+        public boolean isTargetRoute(@Nullable MediaRoute2Info route2Info) {
+            return route2Info != null && mTargetOriginalRouteId.equals(route2Info.getOriginalId());
+        }
+
+        public boolean isTargetRouteIdInList(@NonNull List<String> routeOriginalIdList) {
+            return routeOriginalIdList.stream().anyMatch(mTargetOriginalRouteId::equals);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index 386657e..71cbcb9 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -103,13 +103,14 @@
     public void requestCreateSession(
             long requestId,
             String packageName,
-            String routeId,
+            String routeOriginalId,
             Bundle sessionHints,
             @RoutingSessionInfo.TransferReason int transferReason,
             @NonNull UserHandle transferInitiatorUserHandle,
             @NonNull String transferInitiatorPackageName) {
         if (mConnectionReady) {
-            mActiveConnection.requestCreateSession(requestId, packageName, routeId, sessionHints);
+            mActiveConnection.requestCreateSession(
+                    requestId, packageName, routeOriginalId, sessionHints);
             updateBinding();
         }
     }
@@ -153,35 +154,35 @@
             long requestId,
             @NonNull UserHandle transferInitiatorUserHandle,
             @NonNull String transferInitiatorPackageName,
-            String sessionId,
-            String routeId,
+            String sessionOriginalId,
+            String routeOriginalId,
             @RoutingSessionInfo.TransferReason int transferReason) {
         if (mConnectionReady) {
-            mActiveConnection.transferToRoute(requestId, sessionId, routeId);
+            mActiveConnection.transferToRoute(requestId, sessionOriginalId, routeOriginalId);
         }
     }
 
     @Override
-    public void setRouteVolume(long requestId, String routeId, int volume) {
+    public void setRouteVolume(long requestId, String routeOriginalId, int volume) {
         if (mConnectionReady) {
-            mActiveConnection.setRouteVolume(requestId, routeId, volume);
+            mActiveConnection.setRouteVolume(requestId, routeOriginalId, volume);
             updateBinding();
         }
     }
 
     @Override
-    public void setSessionVolume(long requestId, String sessionId, int volume) {
+    public void setSessionVolume(long requestId, String sessionOriginalId, int volume) {
         if (mConnectionReady) {
-            mActiveConnection.setSessionVolume(requestId, sessionId, volume);
+            mActiveConnection.setSessionVolume(requestId, sessionOriginalId, volume);
             updateBinding();
         }
     }
 
     @Override
-    public void prepareReleaseSession(@NonNull String sessionId) {
+    public void prepareReleaseSession(@NonNull String sessionUniqueId) {
         synchronized (mLock) {
             for (RoutingSessionInfo session : mSessionInfos) {
-                if (TextUtils.equals(session.getId(), sessionId)) {
+                if (TextUtils.equals(session.getId(), sessionUniqueId)) {
                     mSessionInfos.remove(session);
                     mReleasingSessions.add(session);
                     break;
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 6ce3ab4..6b409ee 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -79,12 +79,15 @@
             new AudioManagerBroadcastReceiver();
 
     private final Object mRequestLock = new Object();
+
     @GuardedBy("mRequestLock")
-    private volatile SessionCreationRequest mPendingSessionCreationRequest;
+    private volatile SessionCreationOrTransferRequest mPendingSessionCreationOrTransferRequest;
 
     private final Object mTransferLock = new Object();
+
     @GuardedBy("mTransferLock")
-    @Nullable private volatile SessionCreationRequest mPendingTransferRequest;
+    @Nullable
+    private volatile SessionCreationOrTransferRequest mPendingTransferRequest;
 
     SystemMediaRoute2Provider(Context context, UserHandle user, Looper looper) {
         super(COMPONENT_NAME);
@@ -155,20 +158,20 @@
     public void requestCreateSession(
             long requestId,
             String packageName,
-            String routeId,
+            String routeOriginalId,
             Bundle sessionHints,
             @RoutingSessionInfo.TransferReason int transferReason,
             @NonNull UserHandle transferInitiatorUserHandle,
             @NonNull String transferInitiatorPackageName) {
         // Assume a router without MODIFY_AUDIO_ROUTING permission can't request with
         // a route ID different from the default route ID. The service should've filtered.
-        if (TextUtils.equals(routeId, MediaRoute2Info.ROUTE_ID_DEFAULT)) {
+        if (TextUtils.equals(routeOriginalId, MediaRoute2Info.ROUTE_ID_DEFAULT)) {
             mCallback.onSessionCreated(this, requestId, mDefaultSessionInfo);
             return;
         }
 
         if (!Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
-            if (TextUtils.equals(routeId, mSelectedRouteId)) {
+            if (TextUtils.equals(routeOriginalId, mSelectedRouteId)) {
                 RoutingSessionInfo currentSessionInfo;
                 synchronized (mLock) {
                     currentSessionInfo = mSessionInfos.get(0);
@@ -180,14 +183,16 @@
 
         synchronized (mRequestLock) {
             // Handle the previous request as a failure if exists.
-            if (mPendingSessionCreationRequest != null) {
-                mCallback.onRequestFailed(this, mPendingSessionCreationRequest.mRequestId,
+            if (mPendingSessionCreationOrTransferRequest != null) {
+                mCallback.onRequestFailed(
+                        /* provider= */ this,
+                        mPendingSessionCreationOrTransferRequest.mRequestId,
                         MediaRoute2ProviderService.REASON_UNKNOWN_ERROR);
             }
-            mPendingSessionCreationRequest =
-                    new SessionCreationRequest(
+            mPendingSessionCreationOrTransferRequest =
+                    new SessionCreationOrTransferRequest(
                             requestId,
-                            routeId,
+                            routeOriginalId,
                             RoutingSessionInfo.TRANSFER_REASON_FALLBACK,
                             transferInitiatorUserHandle,
                             transferInitiatorPackageName);
@@ -199,7 +204,7 @@
                 transferInitiatorUserHandle,
                 transferInitiatorPackageName,
                 SYSTEM_SESSION_ID,
-                routeId,
+                routeOriginalId,
                 transferReason);
     }
 
@@ -229,15 +234,15 @@
             long requestId,
             @NonNull UserHandle transferInitiatorUserHandle,
             @NonNull String transferInitiatorPackageName,
-            String sessionId,
-            String routeId,
+            String sessionOriginalId,
+            String routeOriginalId,
             @RoutingSessionInfo.TransferReason int transferReason) {
         String selectedDeviceRouteId = mDeviceRouteController.getSelectedRoute().getId();
-        if (TextUtils.equals(routeId, MediaRoute2Info.ROUTE_ID_DEFAULT)) {
+        if (TextUtils.equals(routeOriginalId, MediaRoute2Info.ROUTE_ID_DEFAULT)) {
             if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
                 // Transfer to the default route (which is the selected route). We replace the id to
                 // be the selected route id so that the transfer reason gets updated.
-                routeId = selectedDeviceRouteId;
+                routeOriginalId = selectedDeviceRouteId;
             } else {
                 Log.w(TAG, "Ignoring transfer to " + MediaRoute2Info.ROUTE_ID_DEFAULT);
                 return;
@@ -247,20 +252,20 @@
         if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
             synchronized (mTransferLock) {
                 mPendingTransferRequest =
-                        new SessionCreationRequest(
+                        new SessionCreationOrTransferRequest(
                                 requestId,
-                                routeId,
+                                routeOriginalId,
                                 transferReason,
                                 transferInitiatorUserHandle,
                                 transferInitiatorPackageName);
             }
         }
 
-        String finalRouteId = routeId; // Make a final copy to use it in the lambda.
+        String finalRouteId = routeOriginalId; // Make a final copy to use it in the lambda.
         boolean isAvailableDeviceRoute =
                 mDeviceRouteController.getAvailableRoutes().stream()
                         .anyMatch(it -> it.getId().equals(finalRouteId));
-        boolean isSelectedDeviceRoute = TextUtils.equals(routeId, selectedDeviceRouteId);
+        boolean isSelectedDeviceRoute = TextUtils.equals(routeOriginalId, selectedDeviceRouteId);
 
         if (isSelectedDeviceRoute || isAvailableDeviceRoute) {
             // The requested route is managed by the device route controller. Note that the selected
@@ -268,12 +273,12 @@
             // of the routing session). If the selected device route is transferred to, we need to
             // make the bluetooth routes inactive so that the device route becomes the selected
             // route of the routing session.
-            mDeviceRouteController.transferTo(routeId);
+            mDeviceRouteController.transferTo(routeOriginalId);
             mBluetoothRouteController.transferTo(null);
         } else {
             // The requested route is managed by the bluetooth route controller.
             mDeviceRouteController.transferTo(null);
-            mBluetoothRouteController.transferTo(routeId);
+            mBluetoothRouteController.transferTo(routeOriginalId);
         }
 
         if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()
@@ -283,20 +288,20 @@
     }
 
     @Override
-    public void setRouteVolume(long requestId, String routeId, int volume) {
-        if (!TextUtils.equals(routeId, mSelectedRouteId)) {
+    public void setRouteVolume(long requestId, String routeOriginalId, int volume) {
+        if (!TextUtils.equals(routeOriginalId, mSelectedRouteId)) {
             return;
         }
         mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
     }
 
     @Override
-    public void setSessionVolume(long requestId, String sessionId, int volume) {
+    public void setSessionVolume(long requestId, String sessionOriginalId, int volume) {
         // Do nothing since we don't support grouping volume yet.
     }
 
     @Override
-    public void prepareReleaseSession(String sessionId) {
+    public void prepareReleaseSession(String sessionUniqueId) {
         // Do nothing since the system session persists.
     }
 
@@ -438,7 +443,7 @@
                         boolean isTransferringToTheSelectedRoute =
                                 mPendingTransferRequest.isTargetRoute(selectedRoute);
                         boolean canBePotentiallyTransferred =
-                                mPendingTransferRequest.isInsideOfRoutesList(transferableRoutes);
+                                mPendingTransferRequest.isTargetRouteIdInList(transferableRoutes);
 
                         if (isTransferringToTheSelectedRoute) {
                             transferReason = mPendingTransferRequest.mTransferReason;
@@ -492,20 +497,21 @@
     @GuardedBy("mRequestLock")
     private void reportPendingSessionRequestResultLockedIfNeeded(
             RoutingSessionInfo newSessionInfo) {
-        if (mPendingSessionCreationRequest == null) {
+        if (mPendingSessionCreationOrTransferRequest == null) {
             // No pending request, nothing to report.
             return;
         }
 
-        long pendingRequestId = mPendingSessionCreationRequest.mRequestId;
-        if (TextUtils.equals(mSelectedRouteId, mPendingSessionCreationRequest.mRouteId)) {
+        long pendingRequestId = mPendingSessionCreationOrTransferRequest.mRequestId;
+        if (mPendingSessionCreationOrTransferRequest.mTargetOriginalRouteId.equals(
+                mSelectedRouteId)) {
             if (DEBUG) {
                 Slog.w(
                         TAG,
                         "Session creation success to route "
-                                + mPendingSessionCreationRequest.mRouteId);
+                                + mPendingSessionCreationOrTransferRequest.mTargetOriginalRouteId);
             }
-            mPendingSessionCreationRequest = null;
+            mPendingSessionCreationOrTransferRequest = null;
             mCallback.onSessionCreated(this, pendingRequestId, newSessionInfo);
         } else {
             boolean isRequestedRouteConnectedBtRoute = isRequestedRouteConnectedBtRoute();
@@ -515,16 +521,17 @@
                     Slog.w(
                             TAG,
                             "Session creation failed to route "
-                                    + mPendingSessionCreationRequest.mRouteId);
+                                    + mPendingSessionCreationOrTransferRequest
+                                            .mTargetOriginalRouteId);
                 }
-                mPendingSessionCreationRequest = null;
+                mPendingSessionCreationOrTransferRequest = null;
                 mCallback.onRequestFailed(
                         this, pendingRequestId, MediaRoute2ProviderService.REASON_UNKNOWN_ERROR);
             } else if (DEBUG) {
                 Slog.w(
                         TAG,
                         "Session creation waiting state to route "
-                                + mPendingSessionCreationRequest.mRouteId);
+                                + mPendingSessionCreationOrTransferRequest.mTargetOriginalRouteId);
             }
         }
     }
@@ -535,7 +542,9 @@
         // where two BT routes are active so the transferable routes list is empty.
         // See b/307723189 for context
         for (MediaRoute2Info btRoute : mBluetoothRouteController.getAllBluetoothRoutes()) {
-            if (TextUtils.equals(btRoute.getId(), mPendingSessionCreationRequest.mRouteId)) {
+            if (TextUtils.equals(
+                    btRoute.getId(),
+                    mPendingSessionCreationOrTransferRequest.mTargetOriginalRouteId)) {
                 return true;
             }
         }
@@ -585,51 +594,6 @@
                 mBluetoothRouteController.getClass().getSimpleName());
     }
 
-    private static class SessionCreationRequest {
-        private final long mRequestId;
-        @NonNull private final String mRouteId;
-
-        @RoutingSessionInfo.TransferReason private final int mTransferReason;
-
-        @NonNull private final UserHandle mTransferInitiatorUserHandle;
-        @NonNull private final String mTransferInitiatorPackageName;
-
-        SessionCreationRequest(
-                long requestId,
-                @NonNull String routeId,
-                @RoutingSessionInfo.TransferReason int transferReason,
-                @NonNull UserHandle transferInitiatorUserHandle,
-                @NonNull String transferInitiatorPackageName) {
-            mRequestId = requestId;
-            mRouteId = routeId;
-            mTransferReason = transferReason;
-            mTransferInitiatorUserHandle = transferInitiatorUserHandle;
-            mTransferInitiatorPackageName = transferInitiatorPackageName;
-        }
-
-        private boolean isTargetRoute(@Nullable MediaRoute2Info route2Info) {
-            if (route2Info == null) {
-                return false;
-            }
-
-            return isTargetRoute(route2Info.getId());
-        }
-
-        private boolean isTargetRoute(@Nullable String routeId) {
-            return mRouteId.equals(routeId);
-        }
-
-        private boolean isInsideOfRoutesList(@NonNull List<String> routesList) {
-            for (String routeId : routesList) {
-                if (isTargetRoute(routeId)) {
-                    return true;
-                }
-            }
-
-            return false;
-        }
-    }
-
     void updateVolume() {
         int devices = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
         int volume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index bbb19e3..e3d5c54 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -553,7 +553,8 @@
                             mProjectionGrant.getLaunchCookie() == null ? null
                                     : mProjectionGrant.getLaunchCookie().binder;
                     setReviewedConsentSessionLocked(
-                            ContentRecordingSession.createTaskSession(taskWindowContainerToken));
+                            ContentRecordingSession.createTaskSession(
+                                    taskWindowContainerToken, mProjectionGrant.mTaskId));
                     break;
             }
         }
@@ -977,6 +978,7 @@
         private IBinder mToken;
         private IBinder.DeathRecipient mDeathEater;
         private boolean mRestoreSystemAlertWindow;
+        private int mTaskId = -1;
         private LaunchCookie mLaunchCookie = null;
 
         // Values for tracking token validity.
@@ -1197,12 +1199,26 @@
 
         @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION)
         @Override // Binder call
+        public void setTaskId(int taskId) {
+            setTaskId_enforcePermission();
+            mTaskId = taskId;
+        }
+
+        @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION)
+        @Override // Binder call
         public LaunchCookie getLaunchCookie() {
             getLaunchCookie_enforcePermission();
             return mLaunchCookie;
         }
 
         @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION)
+        @Override // Binder call
+        public int getTaskId() {
+            getTaskId_enforcePermission();
+            return mTaskId;
+        }
+
+        @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_MEDIA_PROJECTION)
         @Override
         public boolean isValid() {
             isValid_enforcePermission();
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 66e61c0..3cc0457 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -16,6 +16,9 @@
 
 package com.android.server.notification;
 
+import static android.service.notification.Condition.STATE_TRUE;
+import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_USER;
+
 import android.app.INotificationManager;
 import android.app.NotificationManager;
 import android.content.ComponentName;
@@ -319,7 +322,20 @@
                 final Condition c = conditions[i];
                 final ConditionRecord r = getRecordLocked(c.id, info.component, true /*create*/);
                 r.info = info;
-                r.condition = c;
+                if (android.app.Flags.modesUi()) {
+                    // if user turned on the mode, ignore the update unless the app also wants the
+                    // mode on. this will update the origin of the mode and let the owner turn it
+                    // off when the context ends
+                    if (r.condition != null && r.condition.source == UPDATE_ORIGIN_USER) {
+                        if (r.condition.state == STATE_TRUE && c.state == STATE_TRUE) {
+                            r.condition = c;
+                        }
+                    } else {
+                        r.condition = c;
+                    }
+                } else {
+                    r.condition = c;
+                }
             }
         }
         final int N = conditions.length;
diff --git a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
index 27b8574..d060f8f2 100644
--- a/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
+++ b/services/core/java/com/android/server/notification/DefaultDeviceEffectsApplier.java
@@ -32,12 +32,13 @@
 import android.service.notification.ZenDeviceEffects;
 import android.service.notification.ZenModeConfig;
 import android.service.notification.ZenModeConfig.ConfigChangeOrigin;
+import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
 
 /** Default implementation for {@link DeviceEffectsApplier}. */
 class DefaultDeviceEffectsApplier implements DeviceEffectsApplier {
-
+    private static final String TAG = "DeviceEffectsApplier";
     private static final String SUPPRESS_AMBIENT_DISPLAY_TOKEN =
             "DefaultDeviceEffectsApplier:SuppressAmbientDisplay";
     private static final int SATURATION_LEVEL_GRAYSCALE = 0;
@@ -75,28 +76,44 @@
         Binder.withCleanCallingIdentity(() -> {
             if (mLastAppliedEffects.shouldSuppressAmbientDisplay()
                     != effects.shouldSuppressAmbientDisplay()) {
-                mPowerManager.suppressAmbientDisplay(SUPPRESS_AMBIENT_DISPLAY_TOKEN,
-                        effects.shouldSuppressAmbientDisplay());
+                try {
+                    mPowerManager.suppressAmbientDisplay(SUPPRESS_AMBIENT_DISPLAY_TOKEN,
+                            effects.shouldSuppressAmbientDisplay());
+                } catch (Exception e) {
+                    Slog.e(TAG, "Could not change AOD override", e);
+                }
             }
 
             if (mLastAppliedEffects.shouldDisplayGrayscale() != effects.shouldDisplayGrayscale()) {
                 if (mColorDisplayManager != null) {
-                    mColorDisplayManager.setSaturationLevel(
-                            effects.shouldDisplayGrayscale() ? SATURATION_LEVEL_GRAYSCALE
-                                    : SATURATION_LEVEL_FULL_COLOR);
+                    try {
+                        mColorDisplayManager.setSaturationLevel(
+                                effects.shouldDisplayGrayscale() ? SATURATION_LEVEL_GRAYSCALE
+                                        : SATURATION_LEVEL_FULL_COLOR);
+                    } catch (Exception e) {
+                        Slog.e(TAG, "Could not change grayscale override", e);
+                    }
                 }
             }
 
             if (mLastAppliedEffects.shouldDimWallpaper() != effects.shouldDimWallpaper()) {
                 if (mWallpaperManager != null) {
-                    mWallpaperManager.setWallpaperDimAmount(
-                            effects.shouldDimWallpaper() ? WALLPAPER_DIM_AMOUNT_DIMMED
-                                    : WALLPAPER_DIM_AMOUNT_NORMAL);
+                    try {
+                        mWallpaperManager.setWallpaperDimAmount(
+                                effects.shouldDimWallpaper() ? WALLPAPER_DIM_AMOUNT_DIMMED
+                                        : WALLPAPER_DIM_AMOUNT_NORMAL);
+                    } catch (Exception e) {
+                        Slog.e(TAG, "Could not change wallpaper override", e);
+                    }
                 }
             }
 
             if (mLastAppliedEffects.shouldUseNightMode() != effects.shouldUseNightMode()) {
-                updateOrScheduleNightMode(effects.shouldUseNightMode(), origin);
+                try {
+                    updateOrScheduleNightMode(effects.shouldUseNightMode(), origin);
+                } catch (Exception e) {
+                    Slog.e(TAG, "Could not change dark theme override", e);
+                }
             }
         });
 
@@ -131,9 +148,13 @@
 
     private void updateNightModeImmediately(boolean useNightMode) {
         Binder.withCleanCallingIdentity(() -> {
-            mUiModeManager.setAttentionModeThemeOverlay(
-                    useNightMode ? MODE_ATTENTION_THEME_OVERLAY_NIGHT
-                            : MODE_ATTENTION_THEME_OVERLAY_OFF);
+            try {
+                mUiModeManager.setAttentionModeThemeOverlay(
+                        useNightMode ? MODE_ATTENTION_THEME_OVERLAY_NIGHT
+                                : MODE_ATTENTION_THEME_OVERLAY_OFF);
+            } catch (Exception e) {
+                Slog.e(TAG, "Could not change wallpaper override", e);
+            }
         });
     }
 
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 3949dfe..1a8e44b 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -1626,8 +1626,8 @@
 
         ApplicationInfo appInfo = null;
         try {
-            appInfo = mContext.getPackageManager().getApplicationInfo(
-                name.getPackageName(), 0);
+            appInfo = mContext.getPackageManager().getApplicationInfoAsUser(
+                name.getPackageName(), 0, userid);
         } catch (NameNotFoundException e) {
             // Ignore if the package doesn't exist we won't be able to bind to the service.
         }
diff --git a/services/core/java/com/android/server/notification/NotificationShellCmd.java b/services/core/java/com/android/server/notification/NotificationShellCmd.java
index 9f3104c..10169d5 100644
--- a/services/core/java/com/android/server/notification/NotificationShellCmd.java
+++ b/services/core/java/com/android/server/notification/NotificationShellCmd.java
@@ -66,7 +66,7 @@
             + "  disallow_listener COMPONENT [user_id (current user if not specified)]\n"
             + "  allow_assistant COMPONENT [user_id (current user if not specified)]\n"
             + "  remove_assistant COMPONENT [user_id (current user if not specified)]\n"
-            + "  set_dnd [on|none (same as on)|priority|alarms|all|off (same as all)]"
+            + "  set_dnd [on|none (same as on)|priority|alarms|all|off (same as all)]\n"
             + "  allow_dnd PACKAGE [user_id (current user if not specified)]\n"
             + "  disallow_dnd PACKAGE [user_id (current user if not specified)]\n"
             + "  reset_assistant_user_set [user_id (current user if not specified)]\n"
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index ae29e1b..454bd20 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -149,6 +149,8 @@
 
     private static final String IMPLICIT_RULE_ID_PREFIX = "implicit_"; // + pkg_name
 
+    private static final int MAX_ICON_RESOURCE_NAME_LENGTH = 1000;
+
     /**
      * Send new activation AutomaticZenRule statuses to apps with a min target SDK version
      */
@@ -2645,7 +2647,13 @@
         requireNonNull(packageName);
         try {
             final Resources res = mPm.getResourcesForApplication(packageName);
-            return res.getResourceName(resId);
+            String resourceName = res.getResourceName(resId);
+            if (resourceName != null && resourceName.length() > MAX_ICON_RESOURCE_NAME_LENGTH) {
+                Slog.e(TAG, "Resource name for ID=" + resId + " in package " + packageName
+                        + " is too long (" + resourceName.length() + "); ignoring it");
+                return null;
+            }
+            return resourceName;
         } catch (PackageManager.NameNotFoundException | Resources.NotFoundException e) {
             Slog.e(TAG, "Resource name for ID=" + resId + " not found in package " + packageName
                     + ". Resource IDs may change when the application is upgraded, and the system"
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
index dd76037..f540f1d 100644
--- a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
+++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
@@ -925,10 +925,11 @@
         }
     }
 
-    @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+    /**
+     * Reset the temporary services set in CTS tests, this method is primarily used to only revert
+     * the changes caused by CTS tests.
+     */
     public void resetTemporaryServices() {
-        mContext.enforceCallingPermission(
-                Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
         synchronized (mLock) {
             if (mTemporaryHandler != null) {
                 mTemporaryHandler.removeMessages(MSG_RESET_TEMPORARY_SERVICE);
diff --git a/services/core/java/com/android/server/pm/InstantAppResolver.java b/services/core/java/com/android/server/pm/InstantAppResolver.java
index 92d6a82..42efd6e 100644
--- a/services/core/java/com/android/server/pm/InstantAppResolver.java
+++ b/services/core/java/com/android/server/pm/InstantAppResolver.java
@@ -28,6 +28,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.ActivityOptions;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
@@ -296,6 +297,9 @@
         if (needsPhaseTwo) {
             intent.setAction(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE);
         } else {
+            ActivityOptions options = ActivityOptions.makeBasic()
+                    .setPendingIntentCreatorBackgroundActivityStartMode(
+                            ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
             // We have all of the data we need; just start the installer without a second phase
             if (failureIntent != null || installFailureActivity != null) {
                 // Intent that is launched if the package couldn't be installed for any reason.
@@ -322,7 +326,7 @@
                                     PendingIntent.FLAG_CANCEL_CURRENT
                                             | PendingIntent.FLAG_ONE_SHOT
                                             | PendingIntent.FLAG_IMMUTABLE,
-                                    null /*bOptions*/, userId);
+                                    options.toBundle(), userId);
                     IntentSender failureSender = new IntentSender(failureIntentTarget);
                     // TODO(b/72700831): remove populating old extra
                     intent.putExtra(Intent.EXTRA_INSTANT_APP_FAILURE, failureSender);
@@ -342,7 +346,7 @@
                                 new String[] { resolvedType },
                                 PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT
                                         | PendingIntent.FLAG_IMMUTABLE,
-                                null /*bOptions*/, userId);
+                                options.toBundle(), userId);
                 IntentSender successSender = new IntentSender(successIntentTarget);
                 intent.putExtra(Intent.EXTRA_INSTANT_APP_SUCCESS, successSender);
             } catch (RemoteException ignore) { /* ignore; same process */ }
diff --git a/services/core/java/com/android/server/pm/KillAppBlocker.java b/services/core/java/com/android/server/pm/KillAppBlocker.java
new file mode 100644
index 0000000..e2901c3
--- /dev/null
+++ b/services/core/java/com/android/server/pm/KillAppBlocker.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm;
+
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+import static android.content.pm.PackageManager.MATCH_ALL;
+import static android.os.Process.INVALID_UID;
+
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.IActivityManager;
+import android.app.IUidObserver;
+import android.app.UidObserver;
+import android.os.Process;
+import android.os.RemoteException;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Use to monitor UIDs are really killed by the {@link IUidObserver}
+ */
+final class KillAppBlocker {
+    private static final int MAX_WAIT_TIMEOUT_MS = 1000;
+    private CountDownLatch mUidsGoneCountDownLatch = new CountDownLatch(1);
+    private List mActiveUids = new ArrayList();
+    private boolean mRegistered = false;
+
+    private final IUidObserver mUidObserver = new UidObserver() {
+        @Override
+        public void onUidGone(int uid, boolean disabled) {
+            synchronized (this) {
+                mActiveUids.remove((Integer) uid);
+
+                if (mActiveUids.size() == 0) {
+                    mUidsGoneCountDownLatch.countDown();
+                }
+            }
+        }
+    };
+
+    void register() {
+        if (!mRegistered) {
+            IActivityManager am = ActivityManager.getService();
+            if (am != null) {
+                try {
+                    am.registerUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_GONE,
+                            ActivityManager.PROCESS_STATE_UNKNOWN, "pm");
+                    mRegistered = true;
+                } catch (RemoteException e) {
+                    // no-op
+                }
+            }
+        }
+    }
+
+    void unregister() {
+        if (mRegistered) {
+            IActivityManager am = ActivityManager.getService();
+            if (am != null) {
+                try {
+                    mRegistered = false;
+                    am.unregisterUidObserver(mUidObserver);
+                } catch (RemoteException e) {
+                    // no-op
+                }
+            }
+        }
+    }
+
+    void waitAppProcessGone(ActivityManagerInternal mAmi, Computer snapshot,
+            UserManagerService userManager, String packageName) {
+        if (!mRegistered) {
+            return;
+        }
+        synchronized (this) {
+            if (mAmi != null) {
+                int[] users = userManager.getUserIds();
+
+                for (int i = 0; i < users.length; i++) {
+                    final int userId = users[i];
+                    final int uid = snapshot.getPackageUidInternal(
+                            packageName, MATCH_ALL, userId, Process.SYSTEM_UID);
+                    if (uid != INVALID_UID) {
+                        if (mAmi.getUidProcessState(uid) != PROCESS_STATE_NONEXISTENT) {
+                            mActiveUids.add(uid);
+                        }
+                    }
+                }
+            }
+        }
+
+        try {
+            mUidsGoneCountDownLatch.await(MAX_WAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            // no-op
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index dec97fb..0d1095f 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -704,7 +704,8 @@
             return false;
         }
 
-        if (isAppOptedOutOfArchiving(packageName, ps.getAppId())) {
+        if (isAppOptedOutOfArchiving(packageName,
+                    UserHandle.getUid(userId, ps.getAppId()))) {
             return false;
         }
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index a5cd821..050d44e 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -1445,6 +1445,15 @@
                     .createEvent(DevicePolicyEnums.UNINSTALL_PACKAGE)
                     .setAdmin(callerPackageName)
                     .write();
+        } else if (PackageInstallerSession.isEmergencyInstallerEnabled(callerPackageName, snapshot,
+                userId, callingUid)) {
+            // Need to clear the calling identity to get DELETE_PACKAGES permission
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                mPm.deletePackageVersioned(versionedPackage, adapter.getBinder(), userId, flags);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
         } else {
             ApplicationInfo appInfo = snapshot.getApplicationInfo(callerPackageName, 0, userId);
             if (appInfo.targetSdkVersion >= Build.VERSION_CODES.P) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 57f6d27..0606563 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -316,14 +316,14 @@
     private static final int INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS = 60000;
 
     /**
-     * If an app being installed targets {@link Build.VERSION_CODES#S API 31} and above, the app
-     * can be installed without user action.
+     * If an app being installed targets {@link Build.VERSION_CODES#TIRAMISU API 33} and above,
+     * the app can be installed without user action.
      * See {@link PackageInstaller.SessionParams#setRequireUserAction} for other conditions required
      * to be satisfied for a silent install.
      */
     @ChangeId
-    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
-    private static final long SILENT_INSTALL_ALLOWED = 265131695L;
+    @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+    private static final long SILENT_INSTALL_ALLOWED = 325888262L;
 
     /**
      * The system supports pre-approval and update ownership features from
@@ -966,7 +966,8 @@
                 getInstallSource().mInstallerPackageName, mInstallerUid);
     }
 
-    private boolean isEmergencyInstallerEnabled(String packageName, Computer snapshot) {
+    static boolean isEmergencyInstallerEnabled(String packageName, Computer snapshot, int userId,
+            int installerUid) {
         final PackageStateInternal ps = snapshot.getPackageStateInternal(packageName);
         if (ps == null || ps.getPkg() == null || !ps.isSystem()) {
             return false;
@@ -974,7 +975,7 @@
         int uid = UserHandle.getUid(userId, ps.getAppId());
         String emergencyInstaller = ps.getPkg().getEmergencyInstaller();
         if (emergencyInstaller == null || !ArrayUtils.contains(
-                snapshot.getPackagesForUid(mInstallerUid), emergencyInstaller)) {
+                snapshot.getPackagesForUid(installerUid), emergencyInstaller)) {
             return false;
         }
         // Only system installers can have an emergency installer
@@ -987,7 +988,7 @@
             return false;
         }
         return (snapshot.checkUidPermission(Manifest.permission.EMERGENCY_INSTALL_PACKAGES,
-                mInstallerUid) == PackageManager.PERMISSION_GRANTED);
+                installerUid) == PackageManager.PERMISSION_GRANTED);
     }
 
     private static final int USER_ACTION_NOT_NEEDED = 0;
@@ -1075,7 +1076,7 @@
                 getInstallerPackageName());
         final boolean isSelfUpdate = targetPackageUid == mInstallerUid;
         final boolean isEmergencyInstall =
-                isEmergencyInstallerEnabled(packageName, snapshot);
+                isEmergencyInstallerEnabled(packageName, snapshot, userId, mInstallerUid);
         final boolean isPermissionGranted = isInstallPermissionGranted
                 || (isUpdatePermissionGranted && isUpdate)
                 || (isSelfUpdatePermissionGranted && isSelfUpdate)
@@ -5153,6 +5154,8 @@
         }
         // Okay to proceed
         synchronized (mLock) {
+            assertCallerIsOwnerOrRoot();
+            assertPreparedAndNotSealedLocked("setPreVerifiedDomains");
             mPreVerifiedDomains = preVerifiedDomains;
         }
     }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f8fceda..679ab65 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3136,13 +3136,18 @@
     void killApplicationSync(String pkgName, @AppIdInt int appId,
             @UserIdInt int userId, String reason, int exitInfoReason) {
         ActivityManagerInternal mAmi = LocalServices.getService(ActivityManagerInternal.class);
-        if (mAmi != null) {
-            if (Thread.holdsLock(mLock)) {
-                // holds PM's lock, go back killApplication to avoid it run into watchdog reset.
-                Slog.e(TAG, "Holds PM's locker, unable kill application synchronized");
-                killApplication(pkgName, appId, userId, reason, exitInfoReason);
-            } else {
+        if (Thread.holdsLock(mLock) || mAmi == null) {
+            // holds PM's lock, go back killApplication to avoid it run into watchdog reset.
+            Slog.e(TAG, "Holds PM's lock, unable kill application synchronized");
+            killApplication(pkgName, appId, userId, reason, exitInfoReason);
+        } else {
+            KillAppBlocker blocker = new KillAppBlocker();
+            try {
+                blocker.register();
                 mAmi.killApplicationSync(pkgName, appId, userId, reason, exitInfoReason);
+                blocker.waitAppProcessGone(mAmi, snapshotComputer(), mUserManager, pkgName);
+            } finally {
+                blocker.unregister();
             }
         }
     }
@@ -4052,9 +4057,6 @@
                 return;
             }
 
-            // Log the metrics when the component state is changed.
-            PackageMetrics.reportComponentStateChanged(computer, componentStateMetricsList, userId);
-
             if (isSynchronous) {
                 flushPackageRestrictionsAsUserInternalLocked(userId);
             } else {
@@ -4074,6 +4076,10 @@
             }
         }
 
+        // Log the metrics when the component state is changed.
+        PackageMetrics.reportComponentStateChanged(snapshotComputer(), componentStateMetricsList,
+                userId);
+
         final long callingId = Binder.clearCallingIdentity();
         try {
             final Computer newSnapshot = snapshotComputer();
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 0a8b2b2..c40563f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -3380,7 +3380,7 @@
         params.sessionParams = sessionParams;
         // Allowlist all permissions by default
         sessionParams.installFlags |= PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS;
-        // Set package source to other by default
+        // Set package source to other by default. Can be overridden by "--package-source"
         sessionParams.setPackageSource(PackageInstaller.PACKAGE_SOURCE_OTHER);
 
         // Encodes one of the states:
@@ -3567,6 +3567,9 @@
                 case "--ignore-dexopt-profile":
                     sessionParams.installFlags |= PackageManager.INSTALL_IGNORE_DEXOPT_PROFILE;
                     break;
+                case "--package-source":
+                    sessionParams.setPackageSource(Integer.parseInt(getNextArg()));
+                    break;
                 default:
                     throw new IllegalArgumentException("Unknown option " + opt);
             }
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 1b6af71..efaa7a8 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -3698,7 +3698,7 @@
             }
             return mTotalTimeUs + (mNesting > 0
                     ? (curBatteryRealtimeUs - mUpdateTimeUs)
-                            / (mTimerPool != null ? mTimerPool.size() : 1)
+                            / (mTimerPool != null && mTimerPool.size() > 0 ? mTimerPool.size() : 1)
                     : 0);
         }
 
diff --git a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
index a96e01b..33ea563 100644
--- a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
@@ -195,6 +195,9 @@
             return null;
         }
 
+        Arrays.fill(mPowerStats.stats, 0);
+        mPowerStats.uidStats.clear();
+
         collectModemActivityInfo();
 
         collectNetworkStats();
@@ -239,16 +242,16 @@
             activityInfo = null;
         }
 
+        if (activityInfo == null) {
+            return;
+        }
+
         ModemActivityInfo deltaInfo = mLastModemActivityInfo == null
-                ? (activityInfo == null ? null : activityInfo.getDelta(activityInfo))
+                ? activityInfo.getDelta(activityInfo)
                 : mLastModemActivityInfo.getDelta(activityInfo);
 
         mLastModemActivityInfo = activityInfo;
 
-        if (deltaInfo == null) {
-            return;
-        }
-
         setTimestamp(deltaInfo.getTimestampMillis());
         mLayout.setDeviceSleepTime(mDeviceStats, deltaInfo.getSleepTimeMillis());
         mLayout.setDeviceIdleTime(mDeviceStats, deltaInfo.getIdleTimeMillis());
@@ -293,8 +296,6 @@
     }
 
     private void collectNetworkStats() {
-        mPowerStats.uidStats.clear();
-
         NetworkStats networkStats = mNetworkStatsSupplier.get();
         if (networkStats == null) {
             return;
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index c8bcc51..e753ce8 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -926,7 +926,9 @@
         }
         Slog.i(TAG, "Enabling rollback for install of " + packageName
                 + ", session:" + session.sessionId
-                + ", rollbackDataPolicy=" + rollbackDataPolicy);
+                + ", rollbackDataPolicy=" + rollbackDataPolicy
+                + ", rollbackId:" + rollback.info.getRollbackId()
+                + ", originalSessionId:" + rollback.getOriginalSessionId());
 
         final String installerPackageName = session.getInstallerPackageName();
         if (!enableRollbackAllowed(installerPackageName, packageName)) {
diff --git a/services/core/java/com/android/server/search/SearchManagerService.java b/services/core/java/com/android/server/search/SearchManagerService.java
index ecfc040..9b39fa1 100644
--- a/services/core/java/com/android/server/search/SearchManagerService.java
+++ b/services/core/java/com/android/server/search/SearchManagerService.java
@@ -61,6 +61,8 @@
     private static final String TAG = "SearchManagerService";
     final Handler mHandler;
 
+    private final MyPackageMonitor mMyPackageMonitor;
+
     public static class Lifecycle extends SystemService {
         private SearchManagerService mService;
 
@@ -95,7 +97,8 @@
      */
     public SearchManagerService(Context context)  {
         mContext = context;
-        new MyPackageMonitor().register(context, null, UserHandle.ALL, true);
+        mMyPackageMonitor = new MyPackageMonitor();
+        mMyPackageMonitor.register(context, null, UserHandle.ALL, true);
         new GlobalSearchProviderObserver(context.getContentResolver());
         mHandler = BackgroundThread.getHandler();
     }
@@ -230,7 +233,6 @@
             if (!shouldRebuildSearchableList(changingUserId)) {
                 return;
             }
-
             synchronized (mSearchables) {
                 // Invalidate the searchable list.
                 Searchables searchables = mSearchables.get(changingUserId);
diff --git a/services/core/java/com/android/server/tracing/TracingServiceProxy.java b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
index c1d92cf..68eb8eb 100644
--- a/services/core/java/com/android/server/tracing/TracingServiceProxy.java
+++ b/services/core/java/com/android/server/tracing/TracingServiceProxy.java
@@ -93,6 +93,7 @@
     private final Context mContext;
     private final PackageManager mPackageManager;
     private final LruCache<ComponentName, ServiceConnector<IMessenger>> mCachedReporterServices;
+    private boolean mServicePublished = false;
 
     private final ITracingServiceProxy.Stub mTracingServiceProxy = new ITracingServiceProxy.Stub() {
         /**
@@ -122,9 +123,12 @@
     public void onStart() {}
 
     @Override
-    public void onBootPhase(int phase) {
-        if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
+    public void onUserUnlocking(@NonNull TargetUser user) {
+        // We need the device storage to be unlocked before we can accept and forward
+        // requests.
+        if (!mServicePublished) {
             publishBinderService(TRACING_SERVICE_PROXY_BINDER_NAME, mTracingServiceProxy);
+            mServicePublished = true;
         }
     }
 
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index e00e813..04db3e8 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -72,7 +72,6 @@
 import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.Xml;
@@ -88,6 +87,7 @@
 import com.android.server.SystemService;
 import com.android.server.servicewatcher.CurrentUserServiceSupplier;
 import com.android.server.servicewatcher.ServiceWatcher;
+import com.android.server.utils.Slogf;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -98,7 +98,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
-
+import java.util.Objects;
 
 /**
  * Manages trust agents and trust listeners.
@@ -275,6 +275,10 @@
             return KeyStoreAuthorization.getInstance();
         }
 
+        AlarmManager getAlarmManager() {
+            return mContext.getSystemService(AlarmManager.class);
+        }
+
         Looper getLooper() {
             return Looper.myLooper();
         }
@@ -293,7 +297,7 @@
         mLockPatternUtils = injector.getLockPatternUtils();
         mKeyStoreAuthorization = injector.getKeyStoreAuthorization();
         mStrongAuthTracker = new StrongAuthTracker(context, injector.getLooper());
-        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+        mAlarmManager = injector.getAlarmManager();
     }
 
     @Override
@@ -362,6 +366,13 @@
     }
 
     private void scheduleTrustTimeout(boolean override, boolean isTrustableTimeout) {
+        if (DEBUG) {
+            Slogf.d(
+                    TAG,
+                    "scheduleTrustTimeout(override=%s, isTrustable=%s)",
+                    override,
+                    isTrustableTimeout);
+        }
         int shouldOverride = override ? 1 : 0;
         int trustableTimeout = isTrustableTimeout ? 1 : 0;
         mHandler.obtainMessage(MSG_SCHEDULE_TRUST_TIMEOUT, shouldOverride,
@@ -370,6 +381,13 @@
 
     private void handleScheduleTrustTimeout(boolean shouldOverride, TimeoutType timeoutType) {
         int userId = mCurrentUser;
+        if (DEBUG) {
+            Slogf.d(
+                    TAG,
+                    "handleScheduleTrustTimeout(shouldOverride=%s, timeoutType=%s)",
+                    shouldOverride,
+                    timeoutType);
+        }
         if (timeoutType == TimeoutType.TRUSTABLE) {
             // don't override the hard timeout unless biometric or knowledge factor authentication
             // occurs which isn't where this is called from. Override the idle timeout what the
@@ -383,6 +401,7 @@
 
     /* Override both the idle and hard trustable timeouts */
     private void refreshTrustableTimers(int userId) {
+        if (DEBUG) Slogf.d(TAG, "refreshTrustableTimers(userId=%s)", userId);
         handleScheduleTrustableTimeouts(userId, true /* overrideIdleTimeout */,
                 true /* overrideHardTimeout */);
     }
@@ -405,13 +424,20 @@
     }
 
     private void handleScheduleTrustedTimeout(int userId, boolean shouldOverride) {
+        if (DEBUG) {
+            Slogf.d(
+                    TAG,
+                    "handleScheduleTrustedTimeout(userId=%s, shouldOverride=%s)",
+                    userId,
+                    shouldOverride);
+        }
         long when = SystemClock.elapsedRealtime() + TRUST_TIMEOUT_IN_MILLIS;
         TrustedTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId);
 
         // Cancel existing trust timeouts for this user if needed.
         if (alarm != null) {
             if (!shouldOverride && alarm.isQueued()) {
-                if (DEBUG) Slog.d(TAG, "Found existing trust timeout alarm. Skipping.");
+                if (DEBUG) Slogf.d(TAG, "Found existing trust timeout alarm. Skipping.");
                 return;
             }
             mAlarmManager.cancel(alarm);
@@ -420,7 +446,9 @@
             mTrustTimeoutAlarmListenerForUser.put(userId, alarm);
         }
 
-        if (DEBUG) Slog.d(TAG, "\tSetting up trust timeout alarm");
+        if (DEBUG) {
+            Slogf.d(TAG, "\tSetting up trust timeout alarm triggering at elapsedRealTime=%s", when);
+        }
         alarm.setQueued(true /* isQueued */);
         mAlarmManager.setExact(
                 AlarmManager.ELAPSED_REALTIME_WAKEUP, when, TRUST_TIMEOUT_ALARM_TAG, alarm,
@@ -434,6 +462,13 @@
     }
 
     private void setUpIdleTimeout(int userId, boolean overrideIdleTimeout) {
+        if (DEBUG) {
+            Slogf.d(
+                    TAG,
+                    "setUpIdleTimeout(userId=%s, overrideIdleTimeout=%s)",
+                    userId,
+                    overrideIdleTimeout);
+        }
         long when = SystemClock.elapsedRealtime() + TRUSTABLE_IDLE_TIMEOUT_IN_MILLIS;
         TrustableTimeoutAlarmListener alarm = mIdleTrustableTimeoutAlarmListenerForUser.get(userId);
         mContext.enforceCallingOrSelfPermission(Manifest.permission.SCHEDULE_EXACT_ALARM, null);
@@ -441,7 +476,7 @@
         // Cancel existing trustable timeouts for this user if needed.
         if (alarm != null) {
             if (!overrideIdleTimeout && alarm.isQueued()) {
-                if (DEBUG) Slog.d(TAG, "Found existing trustable timeout alarm. Skipping.");
+                if (DEBUG) Slogf.d(TAG, "Found existing trustable timeout alarm. Skipping.");
                 return;
             }
             mAlarmManager.cancel(alarm);
@@ -450,7 +485,12 @@
             mIdleTrustableTimeoutAlarmListenerForUser.put(userId, alarm);
         }
 
-        if (DEBUG) Slog.d(TAG, "\tSetting up trustable idle timeout alarm");
+        if (DEBUG) {
+            Slogf.d(
+                    TAG,
+                    "\tSetting up trustable idle timeout alarm triggering at elapsedRealTime=%s",
+                    when);
+        }
         alarm.setQueued(true /* isQueued */);
         mAlarmManager.setExact(
                 AlarmManager.ELAPSED_REALTIME_WAKEUP, when, TRUST_TIMEOUT_ALARM_TAG, alarm,
@@ -458,6 +498,13 @@
     }
 
     private void setUpHardTimeout(int userId, boolean overrideHardTimeout) {
+        if (DEBUG) {
+            Slogf.i(
+                    TAG,
+                    "setUpHardTimeout(userId=%s, overrideHardTimeout=%s)",
+                    userId,
+                    overrideHardTimeout);
+        }
         mContext.enforceCallingOrSelfPermission(Manifest.permission.SCHEDULE_EXACT_ALARM, null);
         TrustableTimeoutAlarmListener alarm = mTrustableTimeoutAlarmListenerForUser.get(userId);
 
@@ -472,7 +519,13 @@
             } else if (overrideHardTimeout) {
                 mAlarmManager.cancel(alarm);
             }
-            if (DEBUG) Slog.d(TAG, "\tSetting up trustable hard timeout alarm");
+            if (DEBUG) {
+                Slogf.d(
+                        TAG,
+                        "\tSetting up trustable hard timeout alarm triggering at "
+                                + "elapsedRealTime=%s",
+                        when);
+            }
             alarm.setQueued(true /* isQueued */);
             mAlarmManager.setExact(
                     AlarmManager.ELAPSED_REALTIME_WAKEUP, when, TRUST_TIMEOUT_ALARM_TAG, alarm,
@@ -503,6 +556,12 @@
         public int hashCode() {
             return component.hashCode() * 31 + userId;
         }
+
+        @Override
+        public String toString() {
+            return String.format(
+                    "AgentInfo{label=%s, component=%s, userId=%s}", label, component, userId);
+        }
     }
 
     private void updateTrustAll() {
@@ -532,6 +591,15 @@
             int flags,
             boolean isFromUnlock,
             @Nullable AndroidFuture<GrantTrustResult> resultCallback) {
+        if (DEBUG) {
+            Slogf.d(
+                    TAG,
+                    "updateTrust(userId=%s, flags=%s, isFromUnlock=%s, resultCallbackPresent=%s)",
+                    userId,
+                    flags,
+                    isFromUnlock,
+                    Objects.isNull(resultCallback));
+        }
         boolean managed = aggregateIsTrustManaged(userId);
         dispatchOnTrustManagedChanged(managed, userId);
         if (mStrongAuthTracker.isTrustAllowedForUser(userId)
@@ -559,27 +627,50 @@
                     (flags & TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0);
             boolean canMoveToTrusted =
                     alreadyUnlocked || isFromUnlock || renewingTrust || isAutomotive();
-            boolean upgradingTrustForCurrentUser = (userId == mCurrentUser);
+            boolean updatingTrustForCurrentUser = (userId == mCurrentUser);
+
+            if (DEBUG) {
+                Slogf.d(
+                        TAG,
+                        "updateTrust: alreadyUnlocked=%s, wasTrusted=%s, wasTrustable=%s, "
+                                + "renewingTrust=%s, canMoveToTrusted=%s, "
+                                + "updatingTrustForCurrentUser=%s",
+                        alreadyUnlocked,
+                        wasTrusted,
+                        wasTrustable,
+                        renewingTrust,
+                        canMoveToTrusted,
+                        updatingTrustForCurrentUser);
+            }
 
             if (trustedByAtLeastOneAgent && wasTrusted) {
                 // no change
                 return;
-            } else if (trustedByAtLeastOneAgent && canMoveToTrusted
-                    && upgradingTrustForCurrentUser) {
+            } else if (trustedByAtLeastOneAgent
+                    && canMoveToTrusted
+                    && updatingTrustForCurrentUser) {
                 pendingTrustState = TrustState.TRUSTED;
-            } else if (trustableByAtLeastOneAgent && (wasTrusted || wasTrustable)
-                    && upgradingTrustForCurrentUser) {
+            } else if (trustableByAtLeastOneAgent
+                    && (wasTrusted || wasTrustable)
+                    && updatingTrustForCurrentUser) {
                 pendingTrustState = TrustState.TRUSTABLE;
             } else {
                 pendingTrustState = TrustState.UNTRUSTED;
             }
+            if (DEBUG) Slogf.d(TAG, "updateTrust: pendingTrustState=%s", pendingTrustState);
 
             mUserTrustState.put(userId, pendingTrustState);
         }
-        if (DEBUG) Slog.d(TAG, "pendingTrustState: " + pendingTrustState);
 
         boolean isNowTrusted = pendingTrustState == TrustState.TRUSTED;
         boolean newlyUnlocked = !alreadyUnlocked && isNowTrusted;
+        if (DEBUG) {
+            Slogf.d(
+                    TAG,
+                    "updateTrust: isNowTrusted=%s, newlyUnlocked=%s",
+                    isNowTrusted,
+                    newlyUnlocked);
+        }
         maybeActiveUnlockRunningChanged(userId);
         dispatchOnTrustChanged(
                 isNowTrusted, newlyUnlocked, userId, flags, getTrustGrantedMessages(userId));
@@ -598,13 +689,13 @@
         boolean shouldSendCallback = newlyUnlocked;
         if (shouldSendCallback) {
             if (resultCallback != null) {
-                if (DEBUG) Slog.d(TAG, "calling back with UNLOCKED_BY_GRANT");
+                if (DEBUG) Slogf.d(TAG, "calling back with UNLOCKED_BY_GRANT");
                 resultCallback.complete(new GrantTrustResult(STATUS_UNLOCKED_BY_GRANT));
             }
         }
 
         if ((wasTrusted || wasTrustable) && pendingTrustState == TrustState.UNTRUSTED) {
-            if (DEBUG) Slog.d(TAG, "Trust was revoked, destroy trustable alarms");
+            if (DEBUG) Slogf.d(TAG, "Trust was revoked, destroy trustable alarms");
             cancelBothTrustableAlarms(userId);
         }
     }
@@ -650,7 +741,7 @@
         try {
             WindowManagerGlobal.getWindowManagerService().lockNow(null);
         } catch (RemoteException e) {
-            Slog.e(TAG, "Error locking screen when called from trust agent");
+            Slogf.e(TAG, "Error locking screen when called from trust agent");
         }
     }
 
@@ -659,8 +750,9 @@
     }
 
     void refreshAgentList(int userIdOrAll) {
-        if (DEBUG) Slog.d(TAG, "refreshAgentList(" + userIdOrAll + ")");
+        if (DEBUG) Slogf.d(TAG, "refreshAgentList(userIdOrAll=%s)", userIdOrAll);
         if (!mTrustAgentsCanRun) {
+            if (DEBUG) Slogf.d(TAG, "Did not refresh agent list because agents cannot run.");
             return;
         }
         if (userIdOrAll != UserHandle.USER_ALL && userIdOrAll < UserHandle.USER_SYSTEM) {
@@ -686,18 +778,30 @@
             if (userInfo == null || userInfo.partial || !userInfo.isEnabled()
                     || userInfo.guestToRemove) continue;
             if (!userInfo.supportsSwitchToByUser()) {
-                if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
-                        + ": switchToByUser=false");
+                if (DEBUG) {
+                    Slogf.d(
+                            TAG,
+                            "refreshAgentList: skipping user %s: switchToByUser=false",
+                            userInfo.id);
+                }
                 continue;
             }
             if (!mActivityManager.isUserRunning(userInfo.id)) {
-                if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
-                        + ": user not started");
+                if (DEBUG) {
+                    Slogf.d(
+                            TAG,
+                            "refreshAgentList: skipping user %s: user not started",
+                            userInfo.id);
+                }
                 continue;
             }
             if (!lockPatternUtils.isSecure(userInfo.id)) {
-                if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
-                        + ": no secure credential");
+                if (DEBUG) {
+                    Slogf.d(
+                            TAG,
+                            "refreshAgentList: skipping user %s: no secure credential",
+                            userInfo.id);
+                }
                 continue;
             }
 
@@ -708,8 +812,12 @@
 
             List<ComponentName> enabledAgents = lockPatternUtils.getEnabledTrustAgents(userInfo.id);
             if (enabledAgents.isEmpty()) {
-                if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
-                        + ": no agents enabled by user");
+                if (DEBUG) {
+                    Slogf.d(
+                            TAG,
+                            "refreshAgentList: skipping user %s: no agents enabled by user",
+                            userInfo.id);
+                }
                 continue;
             }
             List<ResolveInfo> resolveInfos = resolveAllowedTrustAgents(pm, userInfo.id);
@@ -717,9 +825,13 @@
                 ComponentName name = getComponentName(resolveInfo);
 
                 if (!enabledAgents.contains(name)) {
-                    if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping "
-                            + name.flattenToShortString() + " u"+ userInfo.id
-                            + ": not enabled by user");
+                    if (DEBUG) {
+                        Slogf.d(
+                                TAG,
+                                "refreshAgentList: skipping %s u%s: not enabled by user",
+                                name.flattenToShortString(),
+                                userInfo.id);
+                    }
                     continue;
                 }
                 if (disableTrustAgents) {
@@ -727,9 +839,13 @@
                             dpm.getTrustAgentConfiguration(null /* admin */, name, userInfo.id);
                     // Disable agent if no features are enabled.
                     if (config == null || config.isEmpty()) {
-                        if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping "
-                                + name.flattenToShortString() + " u"+ userInfo.id
-                                + ": not allowed by DPM");
+                        if (DEBUG) {
+                            Slogf.d(
+                                    TAG,
+                                    "refreshAgentList: skipping %s u%s: not allowed by DPM",
+                                    name.flattenToShortString(),
+                                    userInfo.id);
+                        }
                         continue;
                     }
                 }
@@ -752,15 +868,26 @@
                 }
 
                 if (directUnlock) {
-                    if (DEBUG) Slog.d(TAG, "refreshAgentList: trustagent " + name
-                            + "of user " + userInfo.id + "can unlock user profile.");
+                    if (DEBUG) {
+                        Slogf.d(
+                                TAG,
+                                "refreshAgentList: trustagent %s of user %s can unlock user "
+                                        + "profile.",
+                                name,
+                                userInfo.id);
+                    }
                 }
 
                 if (!mUserManager.isUserUnlockingOrUnlocked(userInfo.id)
                         && !directUnlock) {
-                    if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
-                            + "'s trust agent " + name + ": FBE still locked and "
-                            + " the agent cannot unlock user profile.");
+                    if (DEBUG) {
+                        Slogf.d(
+                                TAG,
+                                "refreshAgentList: skipping user %s's trust agent %s: FBE still "
+                                        + "locked and the agent cannot unlock user profile.",
+                                userInfo.id,
+                                name);
+                    }
                     continue;
                 }
 
@@ -769,11 +896,16 @@
                     if (flag != StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) {
                         if (flag != StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
                             || !directUnlock) {
-                            if (DEBUG)
-                                Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
-                                    + ": prevented by StrongAuthTracker = 0x"
-                                    + Integer.toHexString(mStrongAuthTracker.getStrongAuthForUser(
-                                    userInfo.id)));
+                            if (DEBUG) {
+                                Slogf.d(
+                                        TAG,
+                                        "refreshAgentList: skipping user %s: prevented by "
+                                                + "StrongAuthTracker = 0x%s",
+                                        userInfo.id,
+                                        Integer.toHexString(
+                                                mStrongAuthTracker.getStrongAuthForUser(
+                                                        userInfo.id)));
+                            }
                             continue;
                         }
                     }
@@ -804,6 +936,15 @@
             }
         }
 
+        if (DEBUG) {
+            Slogf.d(
+                    TAG,
+                    "refreshAgentList: userInfos=%s, obsoleteAgents=%s, trustMayHaveChanged=%s",
+                    userInfos,
+                    obsoleteAgents,
+                    trustMayHaveChanged);
+        }
+
         if (trustMayHaveChanged) {
             if (userIdOrAll == UserHandle.USER_ALL) {
                 updateTrustAll();
@@ -1044,7 +1185,7 @@
             parser = resolveInfo.serviceInfo.loadXmlMetaData(pm,
                     TrustAgentService.TRUST_AGENT_META_DATA);
             if (parser == null) {
-                Slog.w(TAG, "Can't find " + TrustAgentService.TRUST_AGENT_META_DATA + " meta-data");
+                Slogf.w(TAG, "Can't find %s meta-data", TrustAgentService.TRUST_AGENT_META_DATA);
                 return null;
             }
             Resources res = pm.getResourcesForApplication(resolveInfo.serviceInfo.applicationInfo);
@@ -1056,7 +1197,7 @@
             }
             String nodeName = parser.getName();
             if (!"trust-agent".equals(nodeName)) {
-                Slog.w(TAG, "Meta-data does not start with trust-agent tag");
+                Slogf.w(TAG, "Meta-data does not start with trust-agent tag");
                 return null;
             }
             TypedArray sa = res
@@ -1075,7 +1216,11 @@
             if (parser != null) parser.close();
         }
         if (caughtException != null) {
-            Slog.w(TAG, "Error parsing : " + resolveInfo.serviceInfo.packageName, caughtException);
+            Slogf.w(
+                    TAG,
+                    caughtException,
+                    "Error parsing : %s",
+                    resolveInfo.serviceInfo.packageName);
             return null;
         }
         if (cn == null) {
@@ -1242,13 +1387,18 @@
     // Agent dispatch and aggregation
 
     private boolean aggregateIsTrusted(int userId) {
+        if (DEBUG) Slogf.d(TAG, "aggregateIsTrusted(userId=%s)", userId);
         if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) {
+            if (DEBUG) {
+                Slogf.d(TAG, "not trusted because trust not allowed for userId=%s", userId);
+            }
             return false;
         }
         for (int i = 0; i < mActiveAgents.size(); i++) {
             AgentInfo info = mActiveAgents.valueAt(i);
             if (info.userId == userId) {
                 if (info.agent.isTrusted()) {
+                    if (DEBUG) Slogf.d(TAG, "trusted by %s", info);
                     return true;
                 }
             }
@@ -1257,13 +1407,18 @@
     }
 
     private boolean aggregateIsTrustable(int userId) {
+        if (DEBUG) Slogf.d(TAG, "aggregateIsTrustable(userId=%s)", userId);
         if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) {
+            if (DEBUG) {
+                Slogf.d(TAG, "not trustable because trust not allowed for userId=%s", userId);
+            }
             return false;
         }
         for (int i = 0; i < mActiveAgents.size(); i++) {
             AgentInfo info = mActiveAgents.valueAt(i);
             if (info.userId == userId) {
                 if (info.agent.isTrustable()) {
+                    if (DEBUG) Slogf.d(TAG, "trustable by %s", info);
                     return true;
                 }
             }
@@ -1328,20 +1483,31 @@
 
     private boolean aggregateIsTrustManaged(int userId) {
         if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) {
+            if (DEBUG) {
+                Slogf.d(
+                        TAG,
+                        "trust not managed due to trust not being allowed for userId=%s",
+                        userId);
+            }
             return false;
         }
         for (int i = 0; i < mActiveAgents.size(); i++) {
             AgentInfo info = mActiveAgents.valueAt(i);
             if (info.userId == userId) {
                 if (info.agent.isManagingTrust()) {
+                    if (DEBUG) Slogf.d(TAG, "trust managed for userId=%s", userId);
                     return true;
                 }
             }
         }
+        if (DEBUG) Slogf.d(TAG, "trust not managed for userId=%s", userId);
         return false;
     }
 
     private void dispatchUnlockAttempt(boolean successful, int userId) {
+        if (DEBUG) {
+            Slogf.d(TAG, "dispatchUnlockAttempt(successful=%s, userId=%s)", successful, userId);
+        }
         if (successful) {
             mStrongAuthTracker.allowTrustFromUnlock(userId);
             // Allow the presence of trust on a successful unlock attempt to extend unlock
@@ -1359,8 +1525,11 @@
 
     private void dispatchUserRequestedUnlock(int userId, boolean dismissKeyguard) {
         if (DEBUG) {
-            Slog.d(TAG, "dispatchUserRequestedUnlock(user=" + userId + ", dismissKeyguard="
-                    + dismissKeyguard + ")");
+            Slogf.d(
+                    TAG,
+                    "dispatchUserRequestedUnlock(user=%s, dismissKeyguard=%s)",
+                    userId,
+                    dismissKeyguard);
         }
         for (int i = 0; i < mActiveAgents.size(); i++) {
             AgentInfo info = mActiveAgents.valueAt(i);
@@ -1372,7 +1541,7 @@
 
     private void dispatchUserMayRequestUnlock(int userId) {
         if (DEBUG) {
-            Slog.d(TAG, "dispatchUserMayRequestUnlock(user=" + userId + ")");
+            Slogf.d(TAG, "dispatchUserMayRequestUnlock(user=%s)", userId);
         }
         for (int i = 0; i < mActiveAgents.size(); i++) {
             AgentInfo info = mActiveAgents.valueAt(i);
@@ -1405,9 +1574,9 @@
         try {
             listener.onIsActiveUnlockRunningChanged(isRunning, userId);
         } catch (DeadObjectException e) {
-            Slog.d(TAG, "TrustListener dead while trying to notify Active Unlock running state");
+            Slogf.d(TAG, "TrustListener dead while trying to notify Active Unlock running state");
         } catch (RemoteException e) {
-            Slog.e(TAG, "Exception while notifying TrustListener.", e);
+            Slogf.e(TAG, "Exception while notifying TrustListener.", e);
         }
     }
 
@@ -1445,11 +1614,11 @@
                 mTrustListeners.get(i).onTrustChanged(
                         enabled, newlyUnlocked, userId, flags, trustGrantedMessages);
             } catch (DeadObjectException e) {
-                Slog.d(TAG, "Removing dead TrustListener.");
+                Slogf.d(TAG, "Removing dead TrustListener.");
                 mTrustListeners.remove(i);
                 i--;
             } catch (RemoteException e) {
-                Slog.e(TAG, "Exception while notifying TrustListener.", e);
+                Slogf.e(TAG, "Exception while notifying TrustListener.", e);
             }
         }
     }
@@ -1462,11 +1631,11 @@
             try {
                 mTrustListeners.get(i).onEnabledTrustAgentsChanged(userId);
             } catch (DeadObjectException e) {
-                Slog.d(TAG, "Removing dead TrustListener.");
+                Slogf.d(TAG, "Removing dead TrustListener.");
                 mTrustListeners.remove(i);
                 i--;
             } catch (RemoteException e) {
-                Slog.e(TAG, "Exception while notifying TrustListener.", e);
+                Slogf.e(TAG, "Exception while notifying TrustListener.", e);
             }
         }
     }
@@ -1479,11 +1648,11 @@
             try {
                 mTrustListeners.get(i).onTrustManagedChanged(managed, userId);
             } catch (DeadObjectException e) {
-                Slog.d(TAG, "Removing dead TrustListener.");
+                Slogf.d(TAG, "Removing dead TrustListener.");
                 mTrustListeners.remove(i);
                 i--;
             } catch (RemoteException e) {
-                Slog.e(TAG, "Exception while notifying TrustListener.", e);
+                Slogf.e(TAG, "Exception while notifying TrustListener.", e);
             }
         }
     }
@@ -1496,11 +1665,11 @@
             try {
                 mTrustListeners.get(i).onTrustError(message);
             } catch (DeadObjectException e) {
-                Slog.d(TAG, "Removing dead TrustListener.");
+                Slogf.d(TAG, "Removing dead TrustListener.");
                 mTrustListeners.remove(i);
                 i--;
             } catch (RemoteException e) {
-                Slog.e(TAG, "Exception while notifying TrustListener.", e);
+                Slogf.e(TAG, "Exception while notifying TrustListener.", e);
             }
         }
     }
@@ -1535,7 +1704,7 @@
                     && mFingerprintManager.hasEnrolledTemplates(userId)
                     && isWeakOrConvenienceSensor(
                             mFingerprintManager.getSensorProperties().get(0))) {
-                Slog.i(TAG, "User is unlockable by non-strong fingerprint auth");
+                Slogf.i(TAG, "User is unlockable by non-strong fingerprint auth");
                 return true;
             }
 
@@ -1543,7 +1712,7 @@
                     && (disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_FACE) == 0
                     && mFaceManager.hasEnrolledTemplates(userId)
                     && isWeakOrConvenienceSensor(mFaceManager.getSensorProperties().get(0))) {
-                Slog.i(TAG, "User is unlockable by non-strong face auth");
+                Slogf.i(TAG, "User is unlockable by non-strong face auth");
                 return true;
             }
         }
@@ -1551,7 +1720,7 @@
         // Check whether it's possible for the device to be actively unlocked by a trust agent.
         if (getUserTrustStateInner(userId) == TrustState.TRUSTABLE
                 || (isAutomotive() && isTrustUsuallyManagedInternal(userId))) {
-            Slog.i(TAG, "User is unlockable by trust agent");
+            Slogf.i(TAG, "User is unlockable by trust agent");
             return true;
         }
 
@@ -1595,6 +1764,13 @@
     private final IBinder mService = new ITrustManager.Stub() {
         @Override
         public void reportUnlockAttempt(boolean authenticated, int userId) throws RemoteException {
+            if (DEBUG) {
+                Slogf.d(
+                        TAG,
+                        "reportUnlockAttempt(authenticated=%s, userId=%s)",
+                        authenticated,
+                        userId);
+            }
             enforceReportPermission();
             mHandler.obtainMessage(MSG_DISPATCH_UNLOCK_ATTEMPT, authenticated ? 1 : 0, userId)
                     .sendToTarget();
@@ -1611,7 +1787,8 @@
         @Override
         public void reportUserMayRequestUnlock(int userId) throws RemoteException {
             enforceReportPermission();
-            mHandler.obtainMessage(MSG_USER_MAY_REQUEST_UNLOCK, userId, /*arg2=*/ 0).sendToTarget();
+            mHandler.obtainMessage(MSG_USER_MAY_REQUEST_UNLOCK, userId, /* arg2= */ 0)
+                    .sendToTarget();
         }
 
         @Override
@@ -1932,6 +2109,7 @@
         return new Handler(looper) {
             @Override
             public void handleMessage(Message msg) {
+                if (DEBUG) Slogf.d(TAG, "handler: %s", msg.what);
                 switch (msg.what) {
                     case MSG_REGISTER_LISTENER:
                         addListener((ITrustListener) msg.obj);
@@ -2002,8 +2180,24 @@
                         handleScheduleTrustTimeout(shouldOverride, timeoutType);
                         break;
                     case MSG_REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH:
+                        if (DEBUG) {
+                            Slogf.d(TAG, "REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH userId=%s", msg.arg1);
+                        }
                         TrustableTimeoutAlarmListener trustableAlarm =
                                 mTrustableTimeoutAlarmListenerForUser.get(msg.arg1);
+                        if (DEBUG) {
+                            if (trustableAlarm != null) {
+                                Slogf.d(
+                                        TAG,
+                                        "REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH trustable alarm "
+                                                + "isQueued=%s",
+                                        trustableAlarm.mIsQueued);
+                            } else {
+                                Slogf.d(
+                                        TAG,
+                                        "REFRESH_TRUSTABLE_TIMERS_AFTER_AUTH no trustable alarm");
+                            }
+                        }
                         if (trustableAlarm != null && trustableAlarm.isQueued()) {
                             refreshTrustableTimers(msg.arg1);
                         }
@@ -2194,7 +2388,7 @@
             handleAlarm();
             // Only fire if trust can unlock.
             if (mStrongAuthTracker.isTrustAllowedForUser(mUserId)) {
-                if (DEBUG) Slog.d(TAG, "Revoking all trust because of trust timeout");
+                if (DEBUG) Slogf.d(TAG, "Revoking all trust because of trust timeout");
                 mLockPatternUtils.requireStrongAuth(
                         mStrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED, mUserId);
             }
diff --git a/services/core/java/com/android/server/uri/UriPermission.java b/services/core/java/com/android/server/uri/UriPermission.java
index e406eb2..0d1f367 100644
--- a/services/core/java/com/android/server/uri/UriPermission.java
+++ b/services/core/java/com/android/server/uri/UriPermission.java
@@ -223,7 +223,9 @@
             if (mWriteOwners != null && includingOwners) {
                 ownedModeFlags &= ~Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
                 for (UriPermissionOwner r : mWriteOwners) {
-                    r.removeWritePermission(this);
+                    if (r != null) {
+                        r.removeWritePermission(this);
+                    }
                 }
                 mWriteOwners = null;
             }
@@ -348,7 +350,7 @@
         if (mWriteOwners != null) {
             pw.print(prefix);
             pw.println("writeOwners:");
-            for (UriPermissionOwner owner : mReadOwners) {
+            for (UriPermissionOwner owner : mWriteOwners) {
                 pw.print(prefix);
                 pw.println("  * " + owner);
             }
diff --git a/services/core/java/com/android/server/vibrator/HalVibration.java b/services/core/java/com/android/server/vibrator/HalVibration.java
index 8f755f4..f9bad59 100644
--- a/services/core/java/com/android/server/vibrator/HalVibration.java
+++ b/services/core/java/com/android/server/vibrator/HalVibration.java
@@ -133,6 +133,7 @@
         // Save scale values for debugging purposes.
         mScaleLevel = scaler.getScaleLevel(vibrationUsage);
         mAdaptiveScale = scaler.getAdaptiveHapticsScale(vibrationUsage);
+        stats.reportAdaptiveScale(mAdaptiveScale);
 
         // Scale all VibrationEffect instances in given CombinedVibration.
         CombinedVibration newEffect = mEffectToPlay.transform(scaler::scale, vibrationUsage);
diff --git a/services/core/java/com/android/server/vibrator/VibrationStats.java b/services/core/java/com/android/server/vibrator/VibrationStats.java
index 2d00351..dd66809 100644
--- a/services/core/java/com/android/server/vibrator/VibrationStats.java
+++ b/services/core/java/com/android/server/vibrator/VibrationStats.java
@@ -61,6 +61,10 @@
     private int mEndedByUsage;
     private int mInterruptedUsage;
 
+    // Vibration parameters.
+    // Set by VibrationThread only (single-threaded).
+    private float mAdaptiveScale;
+
     // All following counters are set by VibrationThread only (single-threaded):
     // Counts how many times the VibrationEffect was repeated.
     private int mRepeatCount;
@@ -188,6 +192,14 @@
         }
     }
 
+    /** Report the adaptive scale that was applied to this vibration. */
+    void reportAdaptiveScale(float scale) {
+        // Only report adaptive scale if it was set for this vibration.
+        if (Float.compare(scale, VibrationScaler.ADAPTIVE_SCALE_NONE) != 0) {
+            mAdaptiveScale = scale;
+        }
+    }
+
     /** Report the vibration has looped a few more times. */
     void reportRepetition(int loops) {
         mRepeatCount += loops;
@@ -287,6 +299,7 @@
         public final int vibrationType;
         public final int usage;
         public final int status;
+        public final float adaptiveScale;
         public final boolean endedBySameUid;
         public final int endedByUsage;
         public final int interruptedUsage;
@@ -316,6 +329,7 @@
             this.vibrationType = vibrationType;
             this.usage = usage;
             this.status = status.getProtoEnumValue();
+            this.adaptiveScale = stats.mAdaptiveScale;
             endedBySameUid = (uid == stats.mEndedByUid);
             endedByUsage = stats.mEndedByUsage;
             interruptedUsage = stats.mInterruptedUsage;
@@ -376,7 +390,7 @@
                     halOnCount, halOffCount, halPerformCount, halSetAmplitudeCount,
                     halSetExternalControlCount, halSupportedCompositionPrimitivesUsed,
                     halSupportedEffectsUsed, halUnsupportedCompositionPrimitivesUsed,
-                    halUnsupportedEffectsUsed, halCompositionSize, halPwleSize);
+                    halUnsupportedEffectsUsed, halCompositionSize, halPwleSize, adaptiveScale);
         }
 
         private static int[] filteredKeys(SparseBooleanArray supportArray, boolean supported) {
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 09c2493..3dcc7a6 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -1628,6 +1628,12 @@
             mStatus = Vibration.Status.RUNNING;
         }
 
+        public void scale(VibrationScaler scaler, int usage) {
+            scale.scaleLevel = scaler.getScaleLevel(usage);
+            scale.adaptiveHapticsScale = scaler.getAdaptiveHapticsScale(usage);
+            stats.reportAdaptiveScale(scale.adaptiveHapticsScale);
+        }
+
         public void mute() {
             externalVibration.mute();
         }
@@ -2044,9 +2050,7 @@
 
                 mCurrentExternalVibration = vibHolder;
                 vibHolder.linkToDeath();
-                vibHolder.scale.scaleLevel = mVibrationScaler.getScaleLevel(attrs.getUsage());
-                vibHolder.scale.adaptiveHapticsScale =
-                        mVibrationScaler.getAdaptiveHapticsScale(attrs.getUsage());
+                vibHolder.scale(mVibrationScaler, attrs.getUsage());
             }
 
             if (waitForCompletion) {
diff --git a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
index fd4b061..0e0b78f 100644
--- a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
+++ b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java
@@ -466,17 +466,17 @@
     }
 
     /**
-     * @return The {@link WindowInsetsController.Appearance} flags for the top fullscreen opaque
-     * window in the given {@param TYPE}.
+     * @return The {@link WindowInsetsController.Appearance} flags for the top main app window in
+     * the given {@param TYPE}.
      */
     @WindowInsetsController.Appearance
     private int getAppearance(TYPE source) {
         final ActivityRecord topFullscreenActivity = getTopFullscreenActivity(source);
-        final WindowState topFullscreenOpaqueWindow = topFullscreenActivity != null
-                ? topFullscreenActivity.getTopFullscreenOpaqueWindow()
+        final WindowState topFullscreenWindow = topFullscreenActivity != null
+                ? topFullscreenActivity.findMainWindow()
                 : null;
-        if (topFullscreenOpaqueWindow != null) {
-            return topFullscreenOpaqueWindow.mAttrs.insetsFlags.appearance;
+        if (topFullscreenWindow != null) {
+            return topFullscreenWindow.mAttrs.insetsFlags.appearance;
         }
         return 0;
     }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index eb6262c..2d2a88a86 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3964,7 +3964,7 @@
         }
 
         if (isCurrentVisible) {
-            if (isNextNotYetVisible || delayRemoval) {
+            if (isNextNotYetVisible || delayRemoval || (next != null && isInTransition())) {
                 // Add this activity to the list of stopping activities. It will be processed and
                 // destroyed when the next activity reports idle.
                 addToStopping(false /* scheduleIdle */, false /* idleDelayed */,
@@ -7644,6 +7644,8 @@
             // This could only happen when the window is removed from hierarchy. So do not keep its
             // reference anymore.
             mStartingWindow = null;
+            mStartingData = null;
+            mStartingSurface = null;
         }
         if (mChildren.size() == 0 && mVisibleSetFromTransferredStartingWindow) {
             // We set the visible state to true for the token from a transferred starting
@@ -7660,20 +7662,6 @@
         }
     }
 
-    /**
-     * @return The to top most child window for which {@link LayoutParams#isFullscreen()} returns
-     *         true and isn't fully transparent.
-     */
-    WindowState getTopFullscreenOpaqueWindow() {
-        for (int i = mChildren.size() - 1; i >= 0; i--) {
-            final WindowState win = mChildren.get(i);
-            if (win != null && win.mAttrs.isFullscreen() && !win.isFullyTransparent()) {
-                return win;
-            }
-        }
-        return null;
-    }
-
     WindowState findMainWindow() {
         return findMainWindow(true);
     }
@@ -8565,7 +8553,7 @@
         // If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
         // are already calculated in resolveFixedOrientationConfiguration.
         // Don't apply aspect ratio if app is overridden to fullscreen by device user/manufacturer.
-        if (!isLetterboxedForFixedOrientationAndAspectRatio()
+        if (Flags.immersiveAppRepositioning() && !isLetterboxedForFixedOrientationAndAspectRatio()
                 && !mLetterboxUiController.hasFullscreenOverride()) {
             resolveAspectRatioRestriction(newParentConfiguration);
         }
@@ -8582,6 +8570,14 @@
                 computeConfigByResolveHint(resolvedConfig, newParentConfiguration);
             }
         }
+        // If activity in fullscreen mode is letterboxed because of fixed orientation then bounds
+        // are already calculated in resolveFixedOrientationConfiguration, or if in size compat
+        // mode, it should already be calculated in resolveSizeCompatModeConfiguration.
+        // Don't apply aspect ratio if app is overridden to fullscreen by device user/manufacturer.
+        if (!Flags.immersiveAppRepositioning() && !isLetterboxedForFixedOrientationAndAspectRatio()
+                && !mInSizeCompatModeForBounds && !mLetterboxUiController.hasFullscreenOverride()) {
+            resolveAspectRatioRestriction(newParentConfiguration);
+        }
 
         if (isFixedOrientationLetterboxAllowed || compatDisplayInsets != null
                 // In fullscreen, can be letterboxed for aspect ratio.
@@ -8917,7 +8913,11 @@
     }
 
     boolean isImmersiveMode(@NonNull Rect parentBounds) {
-        if (!mResolveConfigHint.mUseOverrideInsetsForConfig) {
+        if (!Flags.immersiveAppRepositioning()) {
+            return false;
+        }
+        if (!mResolveConfigHint.mUseOverrideInsetsForConfig
+                && mWmService.mFlags.mInsetsDecoupledConfiguration) {
             return false;
         }
         final Insets navBarInsets = mDisplayContent.getInsetsStateController()
@@ -9261,17 +9261,35 @@
             @NonNull CompatDisplayInsets compatDisplayInsets) {
         final Configuration resolvedConfig = getResolvedOverrideConfiguration();
         final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
+        final Insets insets;
+        if (mResolveConfigHint.mUseOverrideInsetsForConfig) {
+            // TODO(b/343197837): Add test to verify SCM behaviour with new bound configuration
+            // Insets are decoupled from configuration by default from V+, use legacy
+            // compatibility behaviour for apps targeting SDK earlier than 35
+            // (see applySizeOverrideIfNeeded).
+            insets = Insets.of(mDisplayContent.getDisplayPolicy()
+                    .getDecorInsetsInfo(mDisplayContent.mDisplayFrames.mRotation,
+                            mDisplayContent.mDisplayFrames.mWidth,
+                            mDisplayContent.mDisplayFrames.mHeight).mOverrideNonDecorInsets);
+        } else {
+            insets = Insets.NONE;
+        }
 
         // When an activity needs to be letterboxed because of fixed orientation, use fixed
         // orientation bounds (stored in resolved bounds) instead of parent bounds since the
         // activity will be displayed within them even if it is in size compat mode. They should be
         // saved here before resolved bounds are overridden below.
-        final Rect containerBounds = isAspectRatioApplied()
+        final boolean useResolvedBounds = Flags.immersiveAppRepositioning()
+                ? isAspectRatioApplied() : isLetterboxedForFixedOrientationAndAspectRatio();
+        final Rect containerBounds = useResolvedBounds
                 ? new Rect(resolvedBounds)
                 : newParentConfiguration.windowConfiguration.getBounds();
-        final Rect containerAppBounds = isAspectRatioApplied()
+        final Rect parentAppBounds =
+                newParentConfiguration.windowConfiguration.getAppBounds();
+        parentAppBounds.inset(insets);
+        final Rect containerAppBounds = useResolvedBounds
                 ? new Rect(resolvedConfig.windowConfiguration.getAppBounds())
-                : newParentConfiguration.windowConfiguration.getAppBounds();
+                : parentAppBounds;
 
         final int requestedOrientation = getRequestedConfigurationOrientation();
         final boolean orientationRequested = requestedOrientation != ORIENTATION_UNDEFINED;
diff --git a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
index 4149bd9..351dc3a 100644
--- a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
+++ b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java
@@ -41,7 +41,7 @@
     static final String DOC_LINK = "go/android-asm";
 
     /** Used to determine which version of the ASM logic was used in logs while we iterate */
-    static final int ASM_VERSION = 10;
+    static final int ASM_VERSION = 11;
 
     private static final String NAMESPACE = NAMESPACE_WINDOW_MANAGER;
     private static final String KEY_ASM_PREFIX = "ActivitySecurity__";
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 0e4f033..f91ef1d 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -1099,10 +1099,6 @@
         }
 
         void finishPresentAnimations() {
-            if (!mComposed) {
-                return;
-            }
-
             if (mCloseAdaptor != null) {
                 mCloseAdaptor.mTarget.cancelAnimation();
                 mCloseAdaptor = null;
@@ -1131,8 +1127,10 @@
         }
 
         void clearBackAnimateTarget() {
-            finishPresentAnimations();
-            mComposed = false;
+            if (mComposed) {
+                mComposed = false;
+                finishPresentAnimations();
+            }
             mWaitTransition = false;
             mStartingSurfaceTargetMatch = false;
             mSwitchType = UNKNOWN;
@@ -1270,6 +1268,8 @@
                         .setContainerLayer()
                         .setHidden(false)
                         .setParent(task.getSurfaceControl())
+                        .setCallsite(
+                                "BackWindowAnimationAdaptorWrapper.getOrCreateAnimationTarget")
                         .build();
                 mCloseTransaction = new SurfaceControl.Transaction();
                 mCloseTransaction.reparent(leashSurface, null);
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 207707e..19d7a3c 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -28,8 +28,10 @@
 import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
 import static android.os.Process.INVALID_PID;
 import static android.os.Process.INVALID_UID;
+import static android.os.Process.ROOT_UID;
 import static android.os.Process.SYSTEM_UID;
 import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER;
+import static android.security.Flags.asmOptSystemIntoEnforcement;
 
 import static com.android.server.wm.ActivityStarter.ASM_RESTRICTIONS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ACTIVITY_STARTS;
@@ -385,6 +387,10 @@
                     return BackgroundStartPrivileges.NONE;
                 case MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED:
                     // no explicit choice by the app - let us decide what to do
+                    if (callingUid == ROOT_UID || callingUid == SYSTEM_UID) {
+                        // root and system must always opt in explicitly
+                        return BackgroundStartPrivileges.NONE;
+                    }
                     if (callingPackage != null) {
                         // determine based on the calling/creating package
                         boolean changeEnabled = CompatChanges.isChangeEnabled(
@@ -1457,13 +1463,18 @@
             return bas.matchesSource();
         }
 
+        if (ar.isUid(SYSTEM_UID)) {
+            if (asmOptSystemIntoEnforcement()) {
+                return bas.optedIn(ar);
+            } else {
+                return bas;
+            }
+        }
+
         if (!CompatChanges.isChangeEnabled(ASM_RESTRICTIONS, ar.getUid())) {
             return bas;
         }
 
-        if (ar.isUid(SYSTEM_UID)) {
-            return bas.optedIn(ar);
-        }
 
         String packageName = ar.packageName;
         if (packageName == null) {
@@ -1561,6 +1572,7 @@
         joiner.add(prefix + "Allowed By Grace Period: " + allowedByGracePeriod);
         joiner.add(prefix + "LastResumedActivity: "
                        + recordToString.apply(mService.mLastResumedActivity));
+        joiner.add(prefix + "System opted into enforcement: " + asmOptSystemIntoEnforcement());
 
         if (mTopFinishedActivity != null) {
             joiner.add(prefix + "TopFinishedActivity: " + mTopFinishedActivity.mDebugInfo);
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 22ca82a..1e7de2b 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -286,6 +286,7 @@
                     .setName(name)
                     .setCallsite("createSurfaceForGestureMonitor")
                     .setParent(inputOverlay)
+                    .setCallsite("InputManagerCallback.createSurfaceForGestureMonitor")
                     .build();
         }
     }
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 72f592b..e07b72a 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -33,6 +33,7 @@
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.os.Process.SYSTEM_UID;
 import static android.view.MotionEvent.CLASSIFICATION_MULTI_FINGER_SWIPE;
+import static android.view.WindowInsets.Type.mandatorySystemGestures;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 
@@ -60,6 +61,8 @@
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
+import android.graphics.Insets;
+import android.graphics.Rect;
 import android.os.Environment;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -71,7 +74,9 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
+import android.view.InsetsState;
 import android.view.MotionEvent;
+import android.view.WindowInsets;
 import android.view.WindowManagerPolicyConstants.PointerEventListener;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -208,6 +213,7 @@
     private final HashMap<ComponentName, ActivityInfo> mTmpAvailActCache = new HashMap<>();
     private final HashMap<String, ApplicationInfo> mTmpAvailAppCache = new HashMap<>();
     private final SparseBooleanArray mTmpQuietProfileUserIds = new SparseBooleanArray();
+    private final Rect mTmpRect = new Rect();
 
     // TODO(b/127498985): This is currently a rough heuristic for interaction inside an app
     private final PointerEventListener mListener = new PointerEventListener() {
@@ -229,12 +235,27 @@
                     if (win == null) {
                         return;
                     }
+
+                    // Verify the touch is within the mandatory system gesture inset bounds of the
+                    // window, use the raw insets state to ignore window z-order
+                    final InsetsState insetsState = dc.getInsetsStateController()
+                            .getRawInsetsState();
+                    mTmpRect.set(win.getFrame());
+                    mTmpRect.inset(insetsState.calculateInsets(win.getFrame(),
+                            mandatorySystemGestures(), false /* ignoreVisibility */));
+                    if (!mTmpRect.contains(x, y)) {
+                        return;
+                    }
+
                     // Unfreeze the task list once we touch down in a task
                     final boolean isAppWindowTouch = FIRST_APPLICATION_WINDOW <= win.mAttrs.type
                             && win.mAttrs.type <= LAST_APPLICATION_WINDOW;
                     if (isAppWindowTouch) {
                         final Task stack = mService.getTopDisplayFocusedRootTask();
                         final Task topTask = stack != null ? stack.getTopMostTask() : null;
+                        ProtoLog.i(WM_DEBUG_TASKS, "Resetting frozen recents task list"
+                                + " reason=app touch win=%s x=%d y=%d insetFrame=%s", win, x, y,
+                                mTmpRect);
                         resetFreezeTaskListReordering(topTask);
                     }
                 }
@@ -301,6 +322,8 @@
             mFreezeTaskListReordering = true;
         }
 
+        ProtoLog.i(WM_DEBUG_TASKS, "Setting frozen recents task list");
+
         // Always update the reordering time when this is called to ensure that the timeout
         // is reset
         mService.mH.removeCallbacks(mResetFreezeTaskListOnTimeoutRunnable);
@@ -344,6 +367,7 @@
             final Task focusedStack = mService.getTopDisplayFocusedRootTask();
             final Task topTask = focusedStack != null ? focusedStack.getTopMostTask() : null;
             final Task reorderToEndTask = topTask != null && topTask.hasChild() ? topTask : null;
+            ProtoLog.i(WM_DEBUG_TASKS, "Resetting frozen recents task list reason=timeout");
             resetFreezeTaskListReordering(reorderToEndTask);
         }
     }
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index 05eeeb3..cff40c7 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -140,23 +140,15 @@
     }
 
     StartingSurface createTaskSnapshotSurface(ActivityRecord activity, TaskSnapshot taskSnapshot) {
-        final WindowState topFullscreenOpaqueWindow;
         final Task task = activity.getTask();
         if (task == null) {
             Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find task for activity="
                     + activity);
             return null;
         }
-        final ActivityRecord topFullscreenActivity = task.getTopFullscreenActivity();
-        if (topFullscreenActivity == null) {
-            Slog.w(TAG, "TaskSnapshotSurface.create: Failed to find top fullscreen for task="
-                    + task);
-            return null;
-        }
-        topFullscreenOpaqueWindow = topFullscreenActivity.getTopFullscreenOpaqueWindow();
-        if (topFullscreenOpaqueWindow == null) {
-            Slog.w(TAG, "TaskSnapshotSurface.create: no opaque window in "
-                    + topFullscreenActivity);
+        final WindowState mainWindow = activity.findMainWindow(false);
+        if (mainWindow == null) {
+            Slog.w(TAG, "TaskSnapshotSurface.create: no main window in " + activity);
             return null;
         }
         if (activity.mDisplayContent.getRotation() != taskSnapshot.getRotation()) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index a555388..6c48e95 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1931,6 +1931,9 @@
             if (td.getSystemBarsAppearance() == 0) {
                 td.setSystemBarsAppearance(atd.getSystemBarsAppearance());
             }
+            if (td.getTopOpaqueSystemBarsAppearance() == 0 && r.fillsParent()) {
+                td.setTopOpaqueSystemBarsAppearance(atd.getSystemBarsAppearance());
+            }
             if (td.getNavigationBarColor() == 0) {
                 td.setNavigationBarColor(atd.getNavigationBarColor());
                 td.setEnsureNavigationBarContrastWhenTransparent(
@@ -3624,14 +3627,15 @@
         // If the developer has persist a different configuration, we need to override it to the
         // starting window because persisted configuration does not effect to Task.
         info.taskInfo.configuration.setTo(activity.getConfiguration());
-        final ActivityRecord topFullscreenActivity = getTopFullscreenActivity();
-        if (topFullscreenActivity != null) {
-            final WindowState topFullscreenOpaqueWindow =
-                    topFullscreenActivity.getTopFullscreenOpaqueWindow();
-            if (topFullscreenOpaqueWindow != null) {
-                info.topOpaqueWindowInsetsState =
-                        topFullscreenOpaqueWindow.getInsetsStateWithVisibilityOverride();
-                info.topOpaqueWindowLayoutParams = topFullscreenOpaqueWindow.getAttrs();
+        if (!Flags.drawSnapshotAspectRatioMatch()) {
+            final ActivityRecord topFullscreenActivity = getTopFullscreenActivity();
+            if (topFullscreenActivity != null) {
+                final WindowState mainWindow = topFullscreenActivity.findMainWindow(false);
+                if (mainWindow != null) {
+                    info.topOpaqueWindowInsetsState =
+                            mainWindow.getInsetsStateWithVisibilityOverride();
+                    info.topOpaqueWindowLayoutParams = mainWindow.getAttrs();
+                }
             }
         }
         return info;
@@ -6879,7 +6883,7 @@
 
         private void assignLayer(@NonNull SurfaceControl.Transaction t, int layer) {
             t.setLayer(mContainerSurface, layer);
-            t.setVisibility(mContainerSurface, mOwnerTaskFragment.isVisible());
+            t.setVisibility(mContainerSurface, mOwnerTaskFragment.isVisible() || mIsBoosted);
             for (int i = 0; i < mPendingClientTransactions.size(); i++) {
                 t.merge(mPendingClientTransactions.get(i));
             }
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index d4bbe84..61022cc 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1346,10 +1346,13 @@
 
             // In a multi-resumed environment, like in a freeform device, the top
             // activity can be resumed, but it might not be the focused app.
-            // Set focused app when top activity is resumed
-            if (taskDisplayArea.inMultiWindowMode() && taskDisplayArea.mDisplayContent != null
-                    && taskDisplayArea.mDisplayContent.mFocusedApp != next) {
-                taskDisplayArea.mDisplayContent.setFocusedApp(next);
+            // Set focused app when top activity is resumed. However, we shouldn't do it for a
+            // same task because it can break focused state. (e.g. activity embedding)
+            if (taskDisplayArea.inMultiWindowMode() && taskDisplayArea.mDisplayContent != null) {
+                final ActivityRecord focusedApp = taskDisplayArea.mDisplayContent.mFocusedApp;
+                if (focusedApp == null || focusedApp.getTask() != next.getTask()) {
+                    taskDisplayArea.mDisplayContent.setFocusedApp(next);
+                }
             }
             ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Top activity "
                     + "resumed %s", next);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 7ec31d5..c972eee 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -236,6 +236,8 @@
     @VisibleForTesting
     ArrayList<Runnable> mTransactionCompletedListeners = null;
 
+    private ArrayList<Runnable> mTransitionEndedListeners = null;
+
     /** Custom activity-level animation options and callbacks. */
     private TransitionInfo.AnimationOptions mOverrideOptions;
     private IRemoteCallback mClientAnimationStartCallback = null;
@@ -1473,6 +1475,18 @@
         mController.mSnapshotController.onTransitionFinish(mType, mTargets);
         // Resume snapshot persist thread after snapshot controller analysis this transition.
         mController.updateAnimatingState();
+
+        invokeTransitionEndedListeners();
+    }
+
+    private void invokeTransitionEndedListeners() {
+        if (mTransitionEndedListeners == null) {
+            return;
+        }
+        for (int i = 0; i < mTransitionEndedListeners.size(); i++) {
+            mTransitionEndedListeners.get(i).run();
+        }
+        mTransitionEndedListeners = null;
     }
 
     private void commitConfigAtEndActivities() {
@@ -1584,6 +1598,7 @@
         // Syncengine abort will call through to onTransactionReady()
         mSyncEngine.abort(mSyncId);
         mController.dispatchLegacyAppTransitionCancelled();
+        invokeTransitionEndedListeners();
     }
 
     /** Immediately moves this to playing even if it isn't started yet. */
@@ -1902,6 +1917,20 @@
     }
 
     /**
+     * Adds a listener that will be executed after the transition is finished or aborted.
+     */
+    void addTransitionEndedListener(Runnable listener) {
+        if (mState != STATE_COLLECTING && mState != STATE_STARTED) {
+            throw new IllegalStateException(
+                    "Can't register listeners if the transition isn't collecting. state=" + mState);
+        }
+        if (mTransitionEndedListeners == null) {
+            mTransitionEndedListeners = new ArrayList<>();
+        }
+        mTransitionEndedListeners.add(listener);
+    }
+
+    /**
      * Checks if the transition contains order changes.
      *
      * This is a shallow check that doesn't account for collection in parallel, unlike
@@ -2639,7 +2668,8 @@
                 }
             }
             final SurfaceControl rootLeash = leashReference.makeAnimationLeash().setName(
-                    "Transition Root: " + leashReference.getName()).build();
+                    "Transition Root: " + leashReference.getName())
+                    .setCallsite("Transition.calculateTransitionRoots").build();
             rootLeash.setUnreleasedWarningCallSite("Transition.calculateTransitionRoots");
             // Update layers to start transaction because we prevent assignment during collect, so
             // the layer of transition root can be correct.
@@ -3757,7 +3787,7 @@
             if (changeInfo.mRotation != wc.mDisplayContent.getRotation()) {
                 // This isn't cheap, so only do it for rotation change.
                 changeInfo.mSnapshotLuma = TransitionAnimation.getBorderLuma(
-                        buffer, screenshotBuffer.getColorSpace());
+                        buffer, screenshotBuffer.getColorSpace(), wc.mSurfaceControl);
             }
             SurfaceControl.Transaction t = wc.mWmService.mTransactionFactory.get();
             TransitionAnimation.configureScreenshotLayer(t, snapshotSurface, screenshotBuffer);
diff --git a/services/core/java/com/android/server/wm/TrustedOverlayHost.java b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
index debe794..9b868be 100644
--- a/services/core/java/com/android/server/wm/TrustedOverlayHost.java
+++ b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
@@ -54,6 +54,7 @@
             final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(null)
                 .setContainerLayer()
                 .setHidden(true)
+                .setCallsite("TrustedOverlayHost.requireOverlaySurfaceControl")
                 .setName("Overlay Host Leash");
 
             mSurfaceControl = b.build();
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b603551..a00b6fc4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1525,7 +1525,7 @@
             InputChannel outInputChannel, InsetsState outInsetsState,
             InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame,
             float[] outSizeCompatScale) {
-        outActiveControls.set(null);
+        outActiveControls.set(null, false /* copyControls */);
         int[] appOp = new int[1];
         final boolean isRoundedCornerOverlay = (attrs.privateFlags
                 & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
@@ -1927,7 +1927,7 @@
             displayContent.getInsetsStateController().updateAboveInsetsState(
                     false /* notifyInsetsChanged */);
 
-            outInsetsState.set(win.getCompatInsetsState(), true /* copySources */);
+            win.fillInsetsState(outInsetsState, true /* copySources */);
             getInsetsSourceControls(win, outActiveControls);
 
             if (win.mLayoutAttached) {
@@ -2317,7 +2317,7 @@
             InsetsState outInsetsState, InsetsSourceControl.Array outActiveControls,
             Bundle outBundle, WindowRelayoutResult outRelayoutResult) {
         if (outActiveControls != null) {
-            outActiveControls.set(null);
+            outActiveControls.set(null, false /* copyControls */);
         }
         int result = 0;
         boolean configChanged = false;
@@ -2680,7 +2680,7 @@
             }
 
             if (outInsetsState != null) {
-                outInsetsState.set(win.getCompatInsetsState(), true /* copySources */);
+                win.fillInsetsState(outInsetsState, true /* copySources */);
             }
 
             ProtoLog.v(WM_DEBUG_FOCUS, "Relayout of %s: focusMayChange=%b",
@@ -2743,25 +2743,14 @@
     }
 
     private void getInsetsSourceControls(WindowState win, InsetsSourceControl.Array outArray) {
-        final InsetsSourceControl[] controls =
-                win.getDisplayContent().getInsetsStateController().getControlsForDispatch(win);
-        if (controls != null) {
-            final int length = controls.length;
-            final InsetsSourceControl[] outControls = new InsetsSourceControl[length];
-            for (int i = 0; i < length; i++) {
-                // We will leave the critical section before returning the leash to the client,
-                // so we need to copy the leash to prevent others release the one that we are
-                // about to return.
-                if (controls[i] != null) {
-                    // This source control is an extra copy if the client is not local. By setting
-                    // PARCELABLE_WRITE_RETURN_VALUE, the leash will be released at the end of
-                    // SurfaceControl.writeToParcel.
-                    outControls[i] = new InsetsSourceControl(controls[i]);
-                    outControls[i].setParcelableFlags(PARCELABLE_WRITE_RETURN_VALUE);
-                }
-            }
-            outArray.set(outControls);
-        }
+        // We will leave the critical section before returning the leash to the client,
+        // so we need to copy the leash to prevent others release the one that we are
+        // about to return.
+        win.fillInsetsSourceControls(outArray, true /* copyControls */);
+        // This source control is an extra copy if the client is not local. By setting
+        // PARCELABLE_WRITE_RETURN_VALUE, the leash will be released at the end of
+        // SurfaceControl.writeToParcel.
+        outArray.setParcelableFlags(PARCELABLE_WRITE_RETURN_VALUE);
     }
 
     private void tryStartExitingAnimation(WindowState win, WindowStateAnimator winAnimator) {
@@ -8619,8 +8608,8 @@
                     return true;
                 }
                 // For a task session, find the activity identified by the launch cookie.
-                final WindowContainerInfo wci = getTaskWindowContainerInfoForLaunchCookie(
-                        incomingSession.getTokenToRecord());
+                final WindowContainerInfo wci =
+                        getTaskWindowContainerInfoForRecordingSession(incomingSession);
                 if (wci == null) {
                     Slog.w(TAG, "Handling a new recording session; unable to find the "
                             + "WindowContainerToken");
@@ -9082,35 +9071,58 @@
     }
 
     /**
-     * Retrieve the {@link WindowContainerInfo} of the task that contains the activity started with
-     * the given launch cookie.
+     * Retrieve the {@link WindowContainerInfo} of the task that was launched for MediaProjection.
      *
-     * @param launchCookie the launch cookie set on the {@link ActivityOptions} when starting an
-     *     activity
+     * @param session the {@link ContentRecordingSession} containing the launch cookie and/or
+     *     task id of the Task started for capture.
      * @return a token representing the task containing the activity started with the given launch
      *     cookie, or {@code null} if the token couldn't be found.
      */
     @VisibleForTesting
     @Nullable
-    WindowContainerInfo getTaskWindowContainerInfoForLaunchCookie(@NonNull IBinder launchCookie) {
-        // Find the activity identified by the launch cookie.
-        final ActivityRecord targetActivity =
-                mRoot.getActivity(activity -> activity.mLaunchCookie == launchCookie);
-        if (targetActivity == null) {
-            Slog.w(TAG, "Unable to find the activity for this launch cookie");
-            return null;
+    WindowContainerInfo getTaskWindowContainerInfoForRecordingSession(
+            @NonNull ContentRecordingSession session) {
+        WindowContainerToken taskWindowContainerToken = null;
+        ActivityRecord targetActivity = null;
+        Task targetTask = null;
+
+        // First attempt to find the launched task by looking for the launched activity with the
+        // matching launch cookie.
+        if (session.getTokenToRecord() != null) {
+            IBinder launchCookie = session.getTokenToRecord();
+            targetActivity = mRoot.getActivity(activity -> activity.mLaunchCookie == launchCookie);
+            if (targetActivity == null) {
+                Slog.w(TAG, "Unable to find the activity for this launch cookie");
+            } else {
+                if (targetActivity.getTask() == null) {
+                    Slog.w(TAG, "Unable to find the task for this launch cookie");
+                } else {
+                    targetTask = targetActivity.getTask();
+                    taskWindowContainerToken = targetTask.mRemoteToken.toWindowContainerToken();
+                }
+            }
         }
-        if (targetActivity.getTask() == null) {
-            Slog.w(TAG, "Unable to find the task for this launch cookie");
-            return null;
+
+        // In the case we can't find an activity with a matching launch cookie, it could be due to
+        // the launched activity being closed, but the launched task is still open, so now attempt
+        // to look for the task directly.
+        if (taskWindowContainerToken == null && session.getTaskId() != -1) {
+            int targetTaskId = session.getTaskId();
+            targetTask = mRoot.getTask(task -> task.isTaskId(targetTaskId));
+            if (targetTask == null) {
+                Slog.w(TAG, "Unable to find the task for this projection");
+            } else {
+                taskWindowContainerToken = targetTask.mRemoteToken.toWindowContainerToken();
+            }
         }
-        WindowContainerToken taskWindowContainerToken =
-                targetActivity.getTask().mRemoteToken.toWindowContainerToken();
+
+        // If we were unable to find the launched task in either fashion, then something must have
+        // wrong (i.e. the task was closed before capture started).
         if (taskWindowContainerToken == null) {
-            Slog.w(TAG, "Unable to find the WindowContainerToken for " + targetActivity.getName());
+            Slog.w(TAG, "Unable to find the WindowContainerToken for ContentRecordingSession");
             return null;
         }
-        return new WindowContainerInfo(targetActivity.getUid(), taskWindowContainerToken);
+        return new WindowContainerInfo(targetTask.effectiveUid, taskWindowContainerToken);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 5e932d4..6221d96 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1613,6 +1613,10 @@
             }
             case OP_TYPE_SET_DECOR_SURFACE_BOOSTED: {
                 if (Flags.activityEmbeddingInteractiveDividerFlag()) {
+                    final Task task = taskFragment.getTask();
+                    if (task == null) {
+                        break;
+                    }
                     final SurfaceControl.Transaction clientTransaction =
                             operation.getSurfaceTransaction();
                     if (clientTransaction != null) {
@@ -1621,10 +1625,22 @@
                         // any invalid operations.
                         clientTransaction.sanitize(caller.mPid, caller.mUid);
                     }
-                    taskFragment.getTask().setDecorSurfaceBoosted(
-                            taskFragment,
-                            operation.getBooleanValue() /* isBoosted */,
-                            clientTransaction);
+
+                    if (transition != null) {
+                        // The decor surface boost/unboost must happen after the transition is
+                        // completed. Otherwise, the decor surface could be moved before Shell
+                        // completes the transition, causing flicker.
+                        transition.addTransitionEndedListener(() ->
+                                task.setDecorSurfaceBoosted(
+                                        taskFragment,
+                                        operation.getBooleanValue() /* isBoosted */,
+                                        clientTransaction));
+                    } else {
+                        task.setDecorSurfaceBoosted(
+                                taskFragment,
+                                operation.getBooleanValue() /* isBoosted */,
+                                clientTransaction);
+                    }
                 }
                 break;
             }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 90c287c..d1efc27 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -188,6 +188,7 @@
 import android.app.ActivityTaskManager;
 import android.app.AppOpsManager;
 import android.app.admin.DevicePolicyCache;
+import android.app.servertransaction.WindowStateInsetsControlChangeItem;
 import android.app.servertransaction.WindowStateResizeItem;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -433,7 +434,10 @@
     /** @see #isLastConfigReportedToClient() */
     private boolean mLastConfigReportedToClient;
 
-    // TODO(b/339380439): Ensure to use the same object for IWindowSession#relayout
+    private final ClientWindowFrames mLastReportedFrames = new ClientWindowFrames();
+
+    private final InsetsState mLastReportedInsetsState = new InsetsState();
+
     private final InsetsSourceControl.Array mLastReportedActiveControls =
             new InsetsSourceControl.Array();
 
@@ -494,8 +498,6 @@
 
     private final WindowFrames mWindowFrames = new WindowFrames();
 
-    private final ClientWindowFrames mClientWindowFrames = new ClientWindowFrames();
-
     /**
      * List of rects where system gestures should be ignored.
      *
@@ -3649,8 +3651,10 @@
                 outFrames.attachedFrame.scale(mInvGlobalScale);
             }
         }
-
         outFrames.compatScale = getCompatScaleForClient();
+        if (mLastReportedFrames != outFrames) {
+            mLastReportedFrames.setTo(outFrames);
+        }
 
         // Note: in the cases where the window is tied to an activity, we should not send a
         // configuration update when the window has requested to be hidden. Doing so can lead to
@@ -3677,6 +3681,25 @@
         mLastConfigReportedToClient = true;
     }
 
+    void fillInsetsState(@NonNull InsetsState outInsetsState, boolean copySources) {
+        outInsetsState.set(getCompatInsetsState(), copySources);
+        if (outInsetsState != mLastReportedInsetsState) {
+            // No need to copy for the recorded.
+            mLastReportedInsetsState.set(outInsetsState, false /* copySources */);
+        }
+    }
+
+    void fillInsetsSourceControls(@NonNull InsetsSourceControl.Array outArray,
+            boolean copyControls) {
+        final InsetsSourceControl[] controls =
+                getDisplayContent().getInsetsStateController().getControlsForDispatch(this);
+        outArray.set(controls, copyControls);
+        if (outArray != mLastReportedActiveControls) {
+            // No need to copy for the recorded.
+            mLastReportedActiveControls.setTo(outArray, false /* copyControls */);
+        }
+    }
+
     void reportResized() {
         // If the activity is scheduled to relaunch, skip sending the resized to ViewRootImpl now
         // since it will be destroyed anyway. This also prevents the client from receiving
@@ -3711,9 +3734,10 @@
 
         final int prevRotation = mLastReportedConfiguration
                 .getMergedConfiguration().windowConfiguration.getRotation();
-        fillClientWindowFramesAndConfiguration(mClientWindowFrames, mLastReportedConfiguration,
+        fillClientWindowFramesAndConfiguration(mLastReportedFrames, mLastReportedConfiguration,
                 mLastReportedActivityWindowInfo, true /* useLatestConfig */,
                 false /* relayoutVisible */);
+        fillInsetsState(mLastReportedInsetsState, false /* copySources */);
         final boolean syncRedraw = shouldSendRedrawForSync();
         final boolean syncWithBuffers = syncRedraw && shouldSyncWithBuffers();
         final boolean reportDraw = syncRedraw || drawPending;
@@ -3733,8 +3757,8 @@
 
         if (Flags.bundleClientTransactionFlag()) {
             getProcess().scheduleClientTransactionItem(
-                    WindowStateResizeItem.obtain(mClient, mClientWindowFrames, reportDraw,
-                            mLastReportedConfiguration, getCompatInsetsState(), forceRelayout,
+                    WindowStateResizeItem.obtain(mClient, mLastReportedFrames, reportDraw,
+                            mLastReportedConfiguration, mLastReportedInsetsState, forceRelayout,
                             alwaysConsumeSystemBars, displayId,
                             syncWithBuffers ? mSyncSeqId : -1, isDragResizing,
                             mLastReportedActivityWindowInfo));
@@ -3742,8 +3766,8 @@
         } else {
             // TODO(b/301870955): cleanup after launch
             try {
-                mClient.resized(mClientWindowFrames, reportDraw, mLastReportedConfiguration,
-                        getCompatInsetsState(), forceRelayout, alwaysConsumeSystemBars, displayId,
+                mClient.resized(mLastReportedFrames, reportDraw, mLastReportedConfiguration,
+                        mLastReportedInsetsState, forceRelayout, alwaysConsumeSystemBars, displayId,
                         syncWithBuffers ? mSyncSeqId : -1, isDragResizing,
                         mLastReportedActivityWindowInfo);
                 onResizePostDispatched(drawPending, prevRotation, displayId);
@@ -3816,13 +3840,17 @@
         if (mRemoved) {
             return;
         }
-        final InsetsStateController stateController =
-                getDisplayContent().getInsetsStateController();
-        mLastReportedActiveControls.set(stateController.getControlsForDispatch(this));
-        try {
-            mClient.insetsControlChanged(getCompatInsetsState(), mLastReportedActiveControls);
-        } catch (RemoteException e) {
-            Slog.w(TAG, "Failed to deliver inset control state change to w=" + this, e);
+        fillInsetsState(mLastReportedInsetsState, false /* copySources */);
+        fillInsetsSourceControls(mLastReportedActiveControls, false /* copyControls */);
+        if (Flags.insetsControlChangedItem()) {
+            getProcess().scheduleClientTransactionItem(WindowStateInsetsControlChangeItem.obtain(
+                    mClient, mLastReportedInsetsState, mLastReportedActiveControls));
+        } else {
+            try {
+                mClient.insetsControlChanged(mLastReportedInsetsState, mLastReportedActiveControls);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to deliver inset control state change to w=" + this, e);
+            }
         }
     }
 
diff --git a/services/core/jni/BroadcastRadio/convert.cpp b/services/core/jni/BroadcastRadio/convert.cpp
index ddbc535..e42f7f8 100644
--- a/services/core/jni/BroadcastRadio/convert.cpp
+++ b/services/core/jni/BroadcastRadio/convert.cpp
@@ -433,7 +433,7 @@
                 gjni.AmBandDescriptor.clazz, gjni.AmBandDescriptor.cstor,
                 region, config.type, config.lowerLimit, config.upperLimit, spacing, am.stereo));
     } else {
-        ALOGE("Unsupported band type: %d", config.type);
+        ALOGE("Unsupported band type: %d", static_cast<int>(config.type));
         return nullptr;
     }
 }
@@ -451,7 +451,7 @@
         return make_javaref(env, env->NewObject(
                 gjni.AmBandConfig.clazz, gjni.AmBandConfig.cstor, descriptor.get()));
     } else {
-        ALOGE("Unsupported band type: %d", config.type);
+        ALOGE("Unsupported band type: %d", static_cast<int>(config.type));
         return nullptr;
     }
 }
@@ -539,9 +539,9 @@
                         item.clockValue.timezoneOffsetInMinutes);
                 break;
             default:
-                ALOGW("invalid metadata type %d", item.type);
+                ALOGW("invalid metadata type %d", static_cast<int>(item.type));
         }
-        ALOGE_IF(status != 0, "Failed inserting metadata %d (of type %d)", key, item.type);
+        ALOGE_IF(status != 0, "Failed inserting metadata %d (of type %d)", key, static_cast<int>(item.type));
     }
 
     return jMetadata;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index e763c9e..669a999 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -1588,7 +1588,7 @@
     private Set<EnforcingAdmin> getEnforcingAdminsOnUser(int userId) {
         synchronized (mLock) {
             return mEnforcingAdmins.contains(userId)
-                    ? mEnforcingAdmins.get(userId) : Collections.emptySet();
+                    ? new HashSet<>(mEnforcingAdmins.get(userId)) : Collections.emptySet();
         }
     }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
index 7483b43..c7fd979 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
@@ -202,9 +202,9 @@
     private String getDefaultSmsPackage() {
         //TODO(b/319449037): Unflag the following change.
         if (Flags.defaultSmsPersonalAppSuspensionFixEnabled()) {
-            return SmsApplication.getDefaultSmsApplicationAsUser(
-                            mContext, /*updateIfNeeded=*/ false, mContext.getUser())
-                    .getPackageName();
+            ComponentName defaultSmsApp = SmsApplication.getDefaultSmsApplicationAsUser(
+                    mContext, /*updateIfNeeded=*/ false, mContext.getUser());
+            return defaultSmsApp != null ? defaultSmsApp.getPackageName() : null;
         } else {
             return Telephony.Sms.getDefaultSmsPackage(mContext);
         }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 927df8b..cfe4e17 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -761,9 +761,6 @@
         }
     }
 
-    private static final long BINDER_CALLBACK_THROTTLE_MS = 10_100L;
-    private long mBinderCallbackLast = -1;
-
     private void run() {
         TimingsTraceAndSlog t = new TimingsTraceAndSlog();
         try {
@@ -968,14 +965,6 @@
         Binder.setTransactionCallback(new IBinderCallback() {
             @Override
             public void onTransactionError(int pid, int code, int flags, int err) {
-
-                final long now = SystemClock.uptimeMillis();
-                if (now < mBinderCallbackLast + BINDER_CALLBACK_THROTTLE_MS) {
-                    Slog.d(TAG, "Too many transaction errors, throttling freezer binder callback.");
-                    return;
-                }
-                mBinderCallbackLast = now;
-                Slog.wtfStack(TAG, "Binder Transaction Error");
                 mActivityManagerService.frozenBinderTransactionDetected(pid, code, flags, err);
             }
         });
diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java
index c16c612..cc340c0 100644
--- a/services/midi/java/com/android/server/midi/MidiService.java
+++ b/services/midi/java/com/android/server/midi/MidiService.java
@@ -287,6 +287,7 @@
         }
 
         public void deviceAdded(Device device) {
+            Log.d(TAG, "deviceAdded() " + device.getUserId() + " userId:" + getUserId());
             // ignore devices that our client cannot access
             if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return;
 
@@ -301,6 +302,7 @@
         }
 
         public void deviceRemoved(Device device) {
+            Log.d(TAG, "deviceRemoved() " + device.getUserId() + " userId:" + getUserId());
             // ignore devices that our client cannot access
             if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return;
 
@@ -315,6 +317,7 @@
         }
 
         public void deviceStatusChanged(Device device, MidiDeviceStatus status) {
+            Log.d(TAG, "deviceStatusChanged() " + device.getUserId() + " userId:" + getUserId());
             // ignore devices that our client cannot access
             if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return;
 
@@ -1303,7 +1306,7 @@
             String[] inputPortNames, String[] outputPortNames, Bundle properties,
             IMidiDeviceServer server, ServiceInfo serviceInfo,
             boolean isPrivate, int uid, int defaultProtocol, int userId) {
-        Log.d(TAG, "addDeviceLocked()" + uid + " type:" + type);
+        Log.d(TAG, "addDeviceLocked() " + uid + " type:" + type + " userId:" + userId);
 
         // Limit the number of devices per app.
         int deviceCountForApp = 0;
diff --git a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
index b0c7073..3bdcd9b 100644
--- a/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
+++ b/services/permission/java/com/android/server/permission/access/appop/AppOpService.kt
@@ -18,6 +18,7 @@
 
 import android.app.AppOpsManager
 import android.companion.virtual.VirtualDeviceManager
+import android.os.Binder
 import android.os.Handler
 import android.os.UserHandle
 import android.permission.PermissionManager
@@ -250,7 +251,9 @@
         ) {
             Slog.w(
                 LOG_TAG,
-                "Cannot set UID mode for runtime permission app op, uid = $uid," +
+                "Cannot set UID mode for runtime permission app op, " +
+                    " callingUid = ${Binder.getCallingUid()}, " +
+                    " uid = $uid," +
                     " code = ${AppOpsManager.opToName(code)}," +
                     " mode = ${AppOpsManager.modeToName(mode)}",
                 RuntimeException()
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 488fe57..9f97648 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -370,18 +370,18 @@
     }
 
     private static void createAndUploadReport(ProfcollectForwardingService pfs) {
-        String reportName;
-        try {
-            reportName = pfs.mIProfcollect.report(pfs.mUsageSetting) + ".zip";
-        } catch (RemoteException e) {
-            Log.e(LOG_TAG, "Failed to create report: " + e.getMessage());
-            return;
-        }
-        if (!pfs.mUploadEnabled) {
-            Log.i(LOG_TAG, "Upload is not enabled.");
-            return;
-        }
         BackgroundThread.get().getThreadHandler().post(() -> {
+            String reportName;
+            try {
+                reportName = pfs.mIProfcollect.report(pfs.mUsageSetting) + ".zip";
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, "Failed to create report: " + e.getMessage());
+                return;
+            }
+            if (!pfs.mUploadEnabled) {
+                Log.i(LOG_TAG, "Upload is not enabled.");
+                return;
+            }
             Intent intent = new Intent()
                     .setPackage("com.android.shell")
                     .setAction("com.android.shell.action.PROFCOLLECT_UPLOAD")
diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp
index a70802a..6c4158e 100644
--- a/services/robotests/Android.bp
+++ b/services/robotests/Android.bp
@@ -64,6 +64,8 @@
     instrumentation_for: "FrameworksServicesLib",
 
     upstream: true,
+
+    strict_mode: false,
 }
 
 filegroup {
diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp
index 569786b..3ace3fb 100644
--- a/services/robotests/backup/Android.bp
+++ b/services/robotests/backup/Android.bp
@@ -68,4 +68,6 @@
 
     upstream: true,
 
+    strict_mode: false,
+
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
index 8db896b..bba4c8d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
@@ -58,6 +58,7 @@
                 .setShouldUseAutoBrightness(shouldUseAutoBrightness)
                 .setShouldUpdateScreenBrightnessSetting(shouldUpdateScreenBrightnessSetting)
                 .setBrightnessAdjustmentFlag(brightnessAdjustmentFlag)
+                .setIsUserInitiatedChange(true)
                 .build();
 
         assertEquals(displayBrightnessState.getBrightness(), brightness, FLOAT_DELTA);
@@ -109,7 +110,9 @@
                 .append("\n    mBrightnessEvent:")
                 .append(Objects.toString(displayBrightnessState.getBrightnessEvent(), "null"))
                 .append("\n    mBrightnessAdjustmentFlag:")
-                .append(displayBrightnessState.getBrightnessAdjustmentFlag());
+                .append(displayBrightnessState.getBrightnessAdjustmentFlag())
+                .append("\n    mIsUserInitiatedChange:")
+                .append(displayBrightnessState.isUserInitiatedChange());
         return sb.toString();
     }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
index 98f572d..8fd1e6b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -1538,6 +1538,8 @@
 
     @Test
     public void testDozeScreenStateOverride_toSupportedOffloadStateFromDoze_DisplayStateChanges() {
+        when(mDisplayManagerFlagsMock.isOffloadDozeOverrideHoldsWakelockEnabled()).thenReturn(true);
+
         // set up.
         int initState = Display.STATE_DOZE;
         int supportedTargetState = Display.STATE_DOZE_SUSPEND;
@@ -1556,10 +1558,15 @@
         mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
         advanceTime(1); // Run updatePowerState
 
+        reset(mHolder.wakelockController);
         mHolder.dpc.overrideDozeScreenState(
                 supportedTargetState, Display.STATE_REASON_DEFAULT_POLICY);
         advanceTime(1); // Run updatePowerState
 
+        // Should get a wakelock to notify powermanager
+        verify(mHolder.wakelockController, atLeastOnce()).acquireWakelock(
+                eq(WakelockController.WAKE_LOCK_UNFINISHED_BUSINESS));
+
         verify(mHolder.displayPowerState)
                 .setScreenState(supportedTargetState, Display.STATE_REASON_DEFAULT_POLICY);
     }
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 12050e1..01ff35f 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -1142,6 +1142,20 @@
     }
 
     @Test
+    public void test_createLocalExternalDisplay_displayManagementEnabled_doesNotCrash()
+            throws Exception {
+        FakeDisplay display = new FakeDisplay(PORT_A);
+        display.info.isInternal = false;
+        setUpDisplay(display);
+        updateAvailableDisplays();
+        mAdapter.registerLocked();
+        when(mSurfaceControlProxy.getDesiredDisplayModeSpecs(display.token)).thenReturn(null);
+        mInjector.getTransmitter().sendHotplug(display, /* connected */ true);
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+        assertThat(mListener.addedDisplays.size()).isEqualTo(1);
+    }
+
+    @Test
     public void test_createLocalExternalDisplay_displayManagementEnabled_shouldHaveDefaultGroup()
             throws Exception {
         FakeDisplay display = new FakeDisplay(PORT_A);
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 c5105e7..323ef6a 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
@@ -121,7 +121,8 @@
                 any(StrategySelectionRequest.class))).thenReturn(displayBrightnessStrategy);
         mDisplayBrightnessController.updateBrightness(displayPowerRequest, targetDisplayState);
         verify(displayBrightnessStrategy).updateBrightness(
-                eq(new StrategyExecutionRequest(displayPowerRequest, DEFAULT_BRIGHTNESS)));
+                eq(new StrategyExecutionRequest(displayPowerRequest, DEFAULT_BRIGHTNESS,
+                        /* userSetBrightnessChanged= */ false)));
         assertEquals(mDisplayBrightnessController.getCurrentDisplayBrightnessStrategy(),
                 displayBrightnessStrategy);
     }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java
index 7a6a911..4d5ff55 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java
@@ -115,7 +115,8 @@
                         .build();
         DisplayBrightnessState updatedDisplayBrightnessState =
                 mAutoBrightnessFallbackStrategy.updateBrightness(
-                        new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+                        new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+                                /* userSetBrightnessChanged= */ false));
         assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
     }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
index afb5a5c..8a33f34 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
@@ -513,9 +513,11 @@
                 .setBrightnessEvent(brightnessEvent)
                 .setBrightnessAdjustmentFlag(BrightnessReason.ADJUSTMENT_AUTO)
                 .setShouldUpdateScreenBrightnessSetting(true)
+                .setIsUserInitiatedChange(true)
                 .build();
         DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
-                .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f));
+                .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
+                        /* userSetBrightnessChanged= */ true));
         assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
     }
 
@@ -560,9 +562,11 @@
                 .setBrightnessEvent(brightnessEvent)
                 .setBrightnessAdjustmentFlag(BrightnessReason.ADJUSTMENT_AUTO_TEMP)
                 .setShouldUpdateScreenBrightnessSetting(true)
+                .setIsUserInitiatedChange(true)
                 .build();
         DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
-                .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f));
+                .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
+                        /* userSetBrightnessChanged= */ true));
         assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
     }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
index 47f1746..3534325 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
@@ -60,7 +60,8 @@
                         .build();
         DisplayBrightnessState updatedDisplayBrightnessState =
                 mBoostBrightnessStrategy.updateBrightness(
-                        new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+                        new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+                                /* userSetBrightnessChanged= */ false));
         assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
     }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
index 9246780..bd6d8be 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
@@ -57,7 +57,8 @@
                         .build();
         DisplayBrightnessState updatedDisplayBrightnessState =
                 mDozeBrightnessModeStrategy.updateBrightness(
-                        new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+                        new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+                                /* userSetBrightnessChanged= */ false));
         assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
     }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
index c4767ae..4cae35d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
@@ -57,10 +57,12 @@
                         .setSdrBrightness(currentBrightness)
                         .setDisplayBrightnessStrategyName(mFallbackBrightnessStrategy.getName())
                         .setShouldUpdateScreenBrightnessSetting(true)
+                        .setIsUserInitiatedChange(true)
                         .build();
         DisplayBrightnessState updatedDisplayBrightnessState =
                 mFallbackBrightnessStrategy.updateBrightness(
-                        new StrategyExecutionRequest(displayPowerRequest, currentBrightness));
+                        new StrategyExecutionRequest(displayPowerRequest, currentBrightness,
+                                /* userSetBrightnessChanged= */ true));
         assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
     }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
index 682c9cc..fdaf8f6 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
@@ -61,7 +61,8 @@
                         .build();
         DisplayBrightnessState updatedDisplayBrightnessState =
                 mFollowerBrightnessStrategy.updateBrightness(
-                        new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+                        new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+                                /* userSetBrightnessChanged= */ false));
         assertEquals(expectedDisplayBrightnessState, updatedDisplayBrightnessState);
     }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java
index ccf6309..e3c2760 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java
@@ -72,7 +72,8 @@
                         .build();
         DisplayBrightnessState updatedDisplayBrightnessState =
                 mOffloadBrightnessStrategy.updateBrightness(
-                        new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+                        new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+                                /* userSetBrightnessChanged= */ false));
         assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
         assertEquals(PowerManager.BRIGHTNESS_INVALID_FLOAT, mOffloadBrightnessStrategy
                 .getOffloadScreenBrightness(), 0.0f);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
index 8e7b463..ebe407b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
@@ -60,7 +60,8 @@
                         .build();
         DisplayBrightnessState updatedDisplayBrightnessState =
                 mOverrideBrightnessStrategy.updateBrightness(
-                        new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+                        new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+                                /* userSetBrightnessChanged= */ false));
         assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
     }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
index e799d0e..4bad569 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
@@ -58,7 +58,8 @@
                         .build();
         DisplayBrightnessState updatedDisplayBrightnessState =
                 mScreenOffBrightnessModeStrategy.updateBrightness(
-                        new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+                        new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+                                /* userSetBrightnessChanged= */ false));
         assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
     }
 }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
index aaada41..5410a20 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
@@ -60,7 +60,8 @@
                         .build();
         DisplayBrightnessState updatedDisplayBrightnessState =
                 mTemporaryBrightnessStrategy.updateBrightness(
-                        new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+                        new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+                                /* userSetBrightnessChanged= */ false));
         assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
     }
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
index cf6146f..34c6ba9 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
@@ -117,11 +117,11 @@
             BaseModeRefreshRateVote(60f), SizeVote(1000, 1000, 1000, 1000), RenderVote(60f, 90f)),
         PREFERRED_REFRESH_RATE(false, 0, 60f, 0f, 0f,
             BaseModeRefreshRateVote(60f), SizeVote(1000, 1000, 1000, 1000), null),
-        PREFERRED_REFRESH_RATE_IGNORED(true, 0, 60f, 0f, 0f,
-            null, null, null),
+        PREFERRED_REFRESH_RATE_IGNORE_BASE_MODE_CONVERSION(true, 0, 60f, 0f, 0f,
+            RequestedRefreshRateVote(60f), null, null),
         PREFERRED_REFRESH_RATE_INVALID(false, 0, 25f, 0f, 0f,
             null, null, null),
         SYNTHETIC_MODE(false, 99, 0f, 0f, 0f,
-            RenderVote(45f, 45f), SizeVote(1000, 1000, 1000, 1000), null),
+            RequestedRefreshRateVote(45f), SizeVote(1000, 1000, 1000, 1000), null),
     }
 }
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/RequestedRefreshRateVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/RequestedRefreshRateVoteTest.kt
new file mode 100644
index 0000000..dbe9e4a
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/RequestedRefreshRateVoteTest.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class RequestedRefreshRateVoteTest {
+
+    @Test
+    fun `updates requestedRefreshRates`() {
+        val refreshRate = 90f
+        val vote = RequestedRefreshRateVote(refreshRate)
+        val summary = createVotesSummary()
+
+        vote.updateSummary(summary)
+
+        assertThat(summary.requestedRefreshRates).hasSize(1)
+        assertThat(summary.requestedRefreshRates).contains(refreshRate)
+    }
+
+    @Test
+    fun `updates requestedRefreshRates with multiple refresh rates`() {
+        val refreshRate1 = 90f
+        val vote1 = RequestedRefreshRateVote(refreshRate1)
+
+        val refreshRate2 = 60f
+        val vote2 = RequestedRefreshRateVote(refreshRate2)
+
+        val summary = createVotesSummary()
+
+        vote1.updateSummary(summary)
+        vote2.updateSummary(summary)
+
+        assertThat(summary.requestedRefreshRates).hasSize(2)
+        assertThat(summary.requestedRefreshRates).containsExactly(refreshRate1, refreshRate2)
+    }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
index 5da1bb6..dd5e1be 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
@@ -152,13 +152,51 @@
 
         assertThat(result.map { it.modeId }).containsExactlyElementsIn(testCase.expectedModeIds)
     }
+
+    @Test
+    fun `summary invalid if has requestedRefreshRate less than minRenederRate`() {
+        val summary = createSummary()
+        summary.requestedRefreshRates = setOf(30f, 90f)
+        summary.minRenderFrameRate = 60f
+        summary.maxRenderFrameRate = 120f
+
+        val result = summary.filterModes(arrayOf(createMode(1, 90f, 90f)))
+
+        assertThat(result).isEmpty()
+    }
+
+    @Test
+    fun `summary invalid if has requestedRefreshRate more than maxRenderFrameRate`() {
+        val summary = createSummary()
+        summary.requestedRefreshRates = setOf(60f, 240f)
+        summary.minRenderFrameRate = 60f
+        summary.maxRenderFrameRate = 120f
+
+        val result = summary.filterModes(arrayOf(createMode(1, 90f, 90f)))
+
+        assertThat(result).isEmpty()
+    }
+
+    @Test
+    fun `summary valid if all requestedRefreshRates inside render rate limits`() {
+        val summary = createSummary()
+        summary.requestedRefreshRates = setOf(60f, 90f)
+        summary.minRenderFrameRate = 60f
+        summary.maxRenderFrameRate = 120f
+
+        val result = summary.filterModes(arrayOf(createMode(1, 90f, 90f)))
+
+        assertThat(result).hasSize(1)
+    }
 }
+
+
 private fun createMode(modeId: Int, refreshRate: Float, vsyncRate: Float): Display.Mode {
     return Display.Mode(modeId, 600, 800, refreshRate, vsyncRate, false,
             FloatArray(0), IntArray(0))
 }
 
-private fun createSummary(supportedModesVoteEnabled: Boolean): VoteSummary {
+private fun createSummary(supportedModesVoteEnabled: Boolean = false): VoteSummary {
     val summary = createVotesSummary(supportedModesVoteEnabled = supportedModesVoteEnabled)
     summary.width = 600
     summary.height = 800
diff --git a/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
index ad68de8..9d8d520 100644
--- a/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/power/batterysaver/BatterySaverStateMachineTest.java
@@ -15,6 +15,8 @@
  */
 package com.android.server.power.batterysaver;
 
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.NULL_DEFAULT;
+
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.inOrder;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -35,12 +37,14 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.PowerManager;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.provider.Settings.Global;
 
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.InOrder;
@@ -65,6 +69,9 @@
 
     private DevicePersistedState mPersistedState;
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(NULL_DEFAULT);
+
     private class DevicePersistedState {
         // Current battery level.
         public int batteryLevel = 100;
@@ -171,6 +178,11 @@
         void triggerDynamicModeNotification() {
             // Do nothing
         }
+
+        @Override
+        void triggerDynamicModeNotificationV2() {
+            // Do nothing
+        }
     }
 
     @Before
diff --git a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
index 0532e04..2a67029 100644
--- a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.trust;
 
+import static android.service.trust.TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE;
+
 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;
@@ -26,12 +28,22 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+
+import static java.util.Collections.singleton;
+
 import android.Manifest;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.AlarmManager;
 import android.app.IActivityManager;
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.ITrustListener;
@@ -65,15 +77,22 @@
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.provider.Settings;
 import android.security.KeyStoreAuthorization;
+import android.service.trust.GrantTrustResult;
+import android.service.trust.ITrustAgentService;
+import android.service.trust.ITrustAgentServiceCallback;
 import android.service.trust.TrustAgentService;
 import android.testing.TestableContext;
+import android.util.Log;
 import android.view.IWindowManager;
 import android.view.WindowManagerGlobal;
 
 import androidx.test.core.app.ApplicationProvider;
 
+import com.android.internal.infra.AndroidFuture;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternUtils.StrongAuthTracker;
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.StrongAuthFlags;
+import com.android.internal.widget.LockSettingsInternal;
 import com.android.modules.utils.testing.ExtendedMockitoRule;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -81,15 +100,19 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
 import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 public class TrustManagerServiceTest {
 
@@ -115,21 +138,28 @@
     private static final int PROFILE_USER_ID = 70;
     private static final long[] PARENT_BIOMETRIC_SIDS = new long[] { 600L, 601L };
     private static final long[] PROFILE_BIOMETRIC_SIDS = new long[] { 700L, 701L };
+    private static final long RENEWABLE_TRUST_DURATION = 10000L;
+    private static final String GRANT_TRUST_MESSAGE = "granted";
+    private static final String TAG = "TrustManagerServiceTest";
 
     private final ArrayList<ResolveInfo> mTrustAgentResolveInfoList = new ArrayList<>();
     private final ArrayList<ComponentName> mKnownTrustAgents = new ArrayList<>();
     private final ArrayList<ComponentName> mEnabledTrustAgents = new ArrayList<>();
+    private final Map<ComponentName, ITrustAgentService.Stub> mMockTrustAgents = new HashMap<>();
 
     private @Mock ActivityManager mActivityManager;
+    private @Mock AlarmManager mAlarmManager;
     private @Mock BiometricManager mBiometricManager;
     private @Mock DevicePolicyManager mDevicePolicyManager;
     private @Mock FaceManager mFaceManager;
     private @Mock FingerprintManager mFingerprintManager;
     private @Mock KeyStoreAuthorization mKeyStoreAuthorization;
     private @Mock LockPatternUtils mLockPatternUtils;
+    private @Mock LockSettingsInternal mLockSettingsInternal;
     private @Mock PackageManager mPackageManager;
     private @Mock UserManager mUserManager;
     private @Mock IWindowManager mWindowManager;
+    private @Mock ITrustListener mTrustListener;
 
     private HandlerThread mHandlerThread;
     private TrustManagerService mService;
@@ -158,6 +188,9 @@
             return null;
         }).when(mLockPatternUtils).setEnabledTrustAgents(any(), eq(TEST_USER_ID));
 
+        LocalServices.removeServiceForTest(LockSettingsInternal.class);
+        LocalServices.addService(LockSettingsInternal.class, mLockSettingsInternal);
+
         ArgumentMatcher<Intent> trustAgentIntentMatcher = new ArgumentMatcher<Intent>() {
             @Override
             public boolean matches(Intent argument) {
@@ -176,6 +209,7 @@
         when(mWindowManager.isKeyguardLocked()).thenReturn(true);
 
         mMockContext.addMockSystemService(ActivityManager.class, mActivityManager);
+        mMockContext.addMockSystemService(AlarmManager.class, mAlarmManager);
         mMockContext.addMockSystemService(BiometricManager.class, mBiometricManager);
         mMockContext.addMockSystemService(FaceManager.class, mFaceManager);
         mMockContext.addMockSystemService(FingerprintManager.class, mFingerprintManager);
@@ -197,6 +231,7 @@
         verify(() -> ServiceManager.addService(eq(Context.TRUST_SERVICE),
                     binderArgumentCaptor.capture(), anyBoolean(), anyInt()));
         mTrustManager = ITrustManager.Stub.asInterface(binderArgumentCaptor.getValue());
+        mTrustManager.registerTrustListener(mTrustListener);
     }
 
     private class MockInjector extends TrustManagerService.Injector {
@@ -215,6 +250,11 @@
         }
 
         @Override
+        AlarmManager getAlarmManager() {
+            return mAlarmManager;
+        }
+
+        @Override
         Looper getLooper() {
             return mHandlerThread.getLooper();
         }
@@ -367,12 +407,10 @@
 
     @Test
     public void reportEnabledTrustAgentsChangedInformsListener() throws RemoteException {
-        final ITrustListener trustListener = mock(ITrustListener.class);
-        mTrustManager.registerTrustListener(trustListener);
         mService.waitForIdle();
         mTrustManager.reportEnabledTrustAgentsChanged(TEST_USER_ID);
         mService.waitForIdle();
-        verify(trustListener).onEnabledTrustAgentsChanged(TEST_USER_ID);
+        verify(mTrustListener).onEnabledTrustAgentsChanged(TEST_USER_ID);
     }
 
     // Tests that when the device is locked for a managed profile with a *unified* challenge, the
@@ -416,6 +454,169 @@
     }
 
     @Test
+    public void testSuccessfulUnlock_bindsTrustAgent() throws Exception {
+        when(mUserManager.getAliveUsers())
+                .thenReturn(List.of(new UserInfo(TEST_USER_ID, "test", UserInfo.FLAG_FULL)));
+        ComponentName trustAgentName =
+                ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
+        ITrustAgentService trustAgentService =
+                setUpTrustAgentWithStrongAuthRequired(
+                        trustAgentName, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+
+        attemptSuccessfulUnlock(TEST_USER_ID);
+        mService.waitForIdle();
+
+        assertThat(getCallback(trustAgentService)).isNotNull();
+    }
+
+    @Test
+    public void testSuccessfulUnlock_notifiesTrustAgent() throws Exception {
+        ComponentName trustAgentName =
+                ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
+        ITrustAgentService trustAgentService =
+                setUpTrustAgentWithStrongAuthRequired(
+                        trustAgentName, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+
+        attemptSuccessfulUnlock(TEST_USER_ID);
+        mService.waitForIdle();
+
+        verify(trustAgentService).onUnlockAttempt(/* successful= */ true);
+    }
+
+    @Test
+    public void testSuccessfulUnlock_notifiesTrustListenerOfChangeInManagedTrust()
+            throws Exception {
+        ComponentName trustAgentName =
+                ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
+        setUpTrustAgentWithStrongAuthRequired(trustAgentName, STRONG_AUTH_NOT_REQUIRED);
+        mService.waitForIdle();
+        Mockito.reset(mTrustListener);
+
+        attemptSuccessfulUnlock(TEST_USER_ID);
+        mService.waitForIdle();
+
+        verify(mTrustListener).onTrustManagedChanged(false, TEST_USER_ID);
+    }
+
+    @Test
+    @Ignore("TODO: b/340891566 - Trustagent always refreshes trustable timer for user 0 on unlock")
+    public void testSuccessfulUnlock_refreshesTrustableTimers() throws Exception {
+        ComponentName trustAgentName =
+                ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
+        ITrustAgentService trustAgent =
+                setUpTrustAgentWithStrongAuthRequired(trustAgentName, STRONG_AUTH_NOT_REQUIRED);
+        setUpRenewableTrust(trustAgent);
+
+        attemptSuccessfulUnlock(TEST_USER_ID);
+        mService.waitForIdle();
+
+        // Idle and hard timeout alarms for first renewable trust granted
+        // Idle timeout alarm refresh for second renewable trust granted
+        // Idle and hard timeout alarms refresh for last report
+        verify(mAlarmManager, times(3))
+                .setExact(
+                        eq(AlarmManager.ELAPSED_REALTIME_WAKEUP),
+                        anyLong(),
+                        anyString(),
+                        any(AlarmManager.OnAlarmListener.class),
+                        any(Handler.class));
+    }
+
+    @Test
+    public void testFailedUnlock_doesNotBindTrustAgent() throws Exception {
+        ComponentName trustAgentName =
+                ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
+        ITrustAgentService trustAgentService =
+                setUpTrustAgentWithStrongAuthRequired(
+                        trustAgentName, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+
+        attemptFailedUnlock(TEST_USER_ID);
+        mService.waitForIdle();
+
+        verify(trustAgentService, never()).setCallback(any());
+    }
+
+    @Test
+    public void testFailedUnlock_notifiesTrustAgent() throws Exception {
+        ComponentName trustAgentName =
+                ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
+        ITrustAgentService trustAgentService =
+                setUpTrustAgentWithStrongAuthRequired(trustAgentName, STRONG_AUTH_NOT_REQUIRED);
+
+        attemptFailedUnlock(TEST_USER_ID);
+        mService.waitForIdle();
+
+        verify(trustAgentService).onUnlockAttempt(/* successful= */ false);
+    }
+
+    @Test
+    public void testFailedUnlock_doesNotNotifyTrustListenerOfChangeInManagedTrust()
+            throws Exception {
+        ComponentName trustAgentName =
+                ComponentName.unflattenFromString("com.android/.SystemTrustAgent");
+        setUpTrustAgentWithStrongAuthRequired(trustAgentName, STRONG_AUTH_NOT_REQUIRED);
+        Mockito.reset(mTrustListener);
+
+        attemptFailedUnlock(TEST_USER_ID);
+        mService.waitForIdle();
+
+        verify(mTrustListener, never()).onTrustManagedChanged(anyBoolean(), anyInt());
+    }
+
+    private void setUpRenewableTrust(ITrustAgentService trustAgent) throws RemoteException {
+        ITrustAgentServiceCallback callback = getCallback(trustAgent);
+        callback.setManagingTrust(true);
+        mService.waitForIdle();
+        attemptSuccessfulUnlock(TEST_USER_ID);
+        mService.waitForIdle();
+        when(mWindowManager.isKeyguardLocked()).thenReturn(false);
+        grantRenewableTrust(callback);
+    }
+
+    private ITrustAgentService setUpTrustAgentWithStrongAuthRequired(
+            ComponentName agentName, @StrongAuthFlags int strongAuthFlags) throws Exception {
+        doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(TEST_USER_ID);
+        addTrustAgent(agentName, true);
+        mLockPatternUtils.setKnownTrustAgents(singleton(agentName), TEST_USER_ID);
+        mLockPatternUtils.setEnabledTrustAgents(singleton(agentName), TEST_USER_ID);
+        when(mUserManager.isUserUnlockingOrUnlocked(TEST_USER_ID)).thenReturn(true);
+        setupStrongAuthTracker(strongAuthFlags, false);
+        mService.waitForIdle();
+        return getOrCreateMockTrustAgent(agentName);
+    }
+
+    private void attemptSuccessfulUnlock(int userId) throws RemoteException {
+        mTrustManager.reportUnlockAttempt(/* successful= */ true, userId);
+    }
+
+    private void attemptFailedUnlock(int userId) throws RemoteException {
+        mTrustManager.reportUnlockAttempt(/* successful= */ false, userId);
+    }
+
+    private void grantRenewableTrust(ITrustAgentServiceCallback callback) throws RemoteException {
+        Log.i(TAG, "Granting trust");
+        AndroidFuture<GrantTrustResult> future = new AndroidFuture<>();
+        callback.grantTrust(
+                GRANT_TRUST_MESSAGE,
+                RENEWABLE_TRUST_DURATION,
+                FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE,
+                future);
+        mService.waitForIdle();
+    }
+
+    /**
+     * Retrieve the ITrustAgentServiceCallback attached to a TrustAgentService after it has been
+     * bound to by the TrustManagerService. Will fail if no binding was established.
+     */
+    private ITrustAgentServiceCallback getCallback(ITrustAgentService trustAgentService)
+            throws RemoteException {
+        ArgumentCaptor<ITrustAgentServiceCallback> callbackCaptor =
+                ArgumentCaptor.forClass(ITrustAgentServiceCallback.class);
+        verify(trustAgentService).setCallback(callbackCaptor.capture());
+        return callbackCaptor.getValue();
+    }
+
+    @Test
     @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
     public void testKeystoreWeakUnlockEnabled_whenWeakFingerprintIsSetupAndAllowed()
             throws Exception {
@@ -637,6 +838,20 @@
         ResolveInfo resolveInfo = new ResolveInfo();
         resolveInfo.serviceInfo = serviceInfo;
         mTrustAgentResolveInfoList.add(resolveInfo);
+        ITrustAgentService.Stub mockService = getOrCreateMockTrustAgent(agentComponentName);
+        mMockContext.addMockService(agentComponentName, mockService);
+        mMockTrustAgents.put(agentComponentName, mockService);
+    }
+
+    private ITrustAgentService.Stub getOrCreateMockTrustAgent(ComponentName agentComponentName) {
+        return mMockTrustAgents.computeIfAbsent(
+                agentComponentName,
+                (componentName) -> {
+                    ITrustAgentService.Stub mockTrustAgent = mock(ITrustAgentService.Stub.class);
+                    when(mockTrustAgent.queryLocalInterface(anyString()))
+                            .thenReturn(mockTrustAgent);
+                    return mockTrustAgent;
+                });
     }
 
     private void bootService() {
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 f971f0e..4e8c755 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -923,6 +923,8 @@
         ResolveInfo resolveInfo1 = installedService1.getResolveInfo();
         AccessibilityServiceInfo installedService2 =
                 mA11yms.getCurrentUserState().mInstalledServices.getLast();
+        // Invokes client change to trigger onUserStateChanged.
+        mA11yms.onClientChangeLocked(false);
 
         // Disables `installedService2`
         when(mMockPackageManager.queryIntentServicesAsUser(any(), anyInt(), anyInt()))
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index c4946f0..8914696 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -432,7 +432,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_RESETTABLE_DYNAMIC_PROPERTIES)
     public void binderDied_resetA11yServiceInfo() {
         final int flag = AccessibilityServiceInfo.FLAG_REPORT_VIEW_IDS;
         setServiceBinding(COMPONENT_NAME);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java
index 52b33db..d4f0d5a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/ProxyManagerTest.java
@@ -593,6 +593,12 @@
         }
 
         @Override
+        public void onMagnificationSystemUIConnectionChanged(boolean connected)
+                throws RemoteException {
+
+        }
+
+        @Override
         public void onMagnificationChanged(int displayId, Region region, MagnificationConfig config)
                 throws RemoteException {
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
index 87fe6cf..0de5807 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationConnectionManagerTest.java
@@ -59,6 +59,7 @@
 import androidx.test.core.app.ApplicationProvider;
 import androidx.test.filters.FlakyTest;
 
+import com.android.compatibility.common.util.TestUtils;
 import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.LocalServices;
 import com.android.server.accessibility.AccessibilityTraceManager;
@@ -76,6 +77,7 @@
  */
 public class MagnificationConnectionManagerTest {
 
+    private static final int WAIT_CONNECTION_TIMEOUT_SECOND = 1;
     private static final int CURRENT_USER_ID = UserHandle.USER_SYSTEM;
     private static final int SERVICE_ID = 1;
 
@@ -115,17 +117,19 @@
     private void stubSetConnection(boolean needDelay) {
         doAnswer((InvocationOnMock invocation) -> {
             final boolean connect = (Boolean) invocation.getArguments()[0];
-            // Simulates setConnection() called by another process.
+            // Use post to simulate setConnection() called by another process.
+            final Context context = ApplicationProvider.getApplicationContext();
             if (needDelay) {
-                final Context context = ApplicationProvider.getApplicationContext();
                 context.getMainThreadHandler().postDelayed(
                         () -> {
                             mMagnificationConnectionManager.setConnection(
                                     connect ? mMockConnection.getConnection() : null);
                         }, 10);
             } else {
-                mMagnificationConnectionManager.setConnection(
-                        connect ? mMockConnection.getConnection() : null);
+                context.getMainThreadHandler().post(() -> {
+                    mMagnificationConnectionManager.setConnection(
+                            connect ? mMockConnection.getConnection() : null);
+                });
             }
             return true;
         }).when(mMockStatusBarManagerInternal).requestMagnificationConnection(anyBoolean());
@@ -629,9 +633,10 @@
     }
 
     @Test
-    public void isConnected_requestConnection_expectedValue() throws RemoteException {
+    public void isConnected_requestConnection_expectedValue() throws Exception {
         mMagnificationConnectionManager.requestConnection(true);
-        assertTrue(mMagnificationConnectionManager.isConnected());
+        TestUtils.waitUntil("connection is not ready", WAIT_CONNECTION_TIMEOUT_SECOND,
+                () -> mMagnificationConnectionManager.isConnected());
 
         mMagnificationConnectionManager.requestConnection(false);
         assertFalse(mMagnificationConnectionManager.isConnected());
diff --git a/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
index e756082..758c84a 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
@@ -28,6 +28,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.res.Resources;
 import android.media.AudioDeviceAttributes;
@@ -37,6 +38,7 @@
 import android.media.AudioSystem;
 import android.media.IAudioDeviceVolumeDispatcher;
 import android.media.VolumeInfo;
+import android.os.PermissionEnforcer;
 import android.os.RemoteException;
 import android.os.test.TestLooper;
 import android.platform.test.annotations.Presubmit;
@@ -98,7 +100,8 @@
 
         mAudioService = new AudioService(mContext, mSpyAudioSystem, mSystemServer,
                 mSettingsAdapter, mAudioVolumeGroupHelper, mMockAudioPolicy,
-                mTestLooper.getLooper()) {
+                mTestLooper.getLooper(), mock(AppOpsManager.class), mock(PermissionEnforcer.class),
+                mock(AudioServerPermissionProvider.class)) {
             @Override
             public int getDeviceForStream(int stream) {
                 return AudioSystem.DEVICE_OUT_SPEAKER;
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
index 3623012..2cb02bd 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
@@ -23,12 +23,14 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
+import android.app.AppOpsManager;
 import android.content.Context;
 import android.media.AudioDeviceAttributes;
 import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
 import android.media.AudioSystem;
 import android.media.VolumeInfo;
+import android.os.PermissionEnforcer;
 import android.os.test.TestLooper;
 import android.platform.test.annotations.RequiresFlagsDisabled;
 import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -75,7 +77,8 @@
         mAudioVolumeGroupHelper = new AudioVolumeGroupHelperBase();
         mAudioService = new AudioService(mContext, mSpyAudioSystem, mSystemServer,
                 mSettingsAdapter, mAudioVolumeGroupHelper, mAudioPolicyMock,
-                mTestLooper.getLooper()) {
+                mTestLooper.getLooper(), mock(AppOpsManager.class), mock(PermissionEnforcer.class),
+                mock(AudioServerPermissionProvider.class)) {
             @Override
             public int getDeviceForStream(int stream) {
                 return AudioSystem.DEVICE_OUT_SPEAKER;
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioServerPermissionProviderTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioServerPermissionProviderTest.java
new file mode 100644
index 0000000..8d772ad
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioServerPermissionProviderTest.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.audio;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.android.media.permission.INativePermissionController;
+import com.android.media.permission.UidPackageState;
+import com.android.server.pm.pkg.PackageState;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public final class AudioServerPermissionProviderTest {
+
+    // Class under test
+    private AudioServerPermissionProvider mPermissionProvider;
+
+    @Rule public final MockitoRule mockito = MockitoJUnit.rule();
+
+    @Mock public INativePermissionController mMockPc;
+
+    @Mock public PackageState mMockPackageStateOne_10000_one;
+    @Mock public PackageState mMockPackageStateTwo_10001_two;
+    @Mock public PackageState mMockPackageStateThree_10000_one;
+    @Mock public PackageState mMockPackageStateFour_10000_three;
+    @Mock public PackageState mMockPackageStateFive_10001_four;
+    @Mock public PackageState mMockPackageStateSix_10000_two;
+
+    public List<UidPackageState> mInitPackageListExpected;
+
+    // Argument matcher which matches that the state is equal even if the package names are out of
+    // order (since they are logically a set).
+    public static final class UidPackageStateMatcher implements ArgumentMatcher<UidPackageState> {
+        private final int mUid;
+        private final List<String> mSortedPackages;
+
+        public UidPackageStateMatcher(int uid, List<String> packageNames) {
+            mUid = uid;
+            if (packageNames != null) {
+                mSortedPackages = new ArrayList(packageNames);
+                Collections.sort(mSortedPackages);
+            } else {
+                mSortedPackages = null;
+            }
+        }
+
+        public UidPackageStateMatcher(UidPackageState toMatch) {
+            this(toMatch.uid, toMatch.packageNames);
+        }
+
+        @Override
+        public boolean matches(UidPackageState state) {
+            if (state == null) return false;
+            if (state.uid != mUid) return false;
+            if ((state.packageNames == null) != (mSortedPackages == null)) return false;
+            var copy = new ArrayList(state.packageNames);
+            Collections.sort(copy);
+            return mSortedPackages.equals(copy);
+        }
+
+        @Override
+        public String toString() {
+            return "Matcher for UidState with uid: " + mUid + ": " + mSortedPackages;
+        }
+    }
+
+    public static final class PackageStateListMatcher
+            implements ArgumentMatcher<List<UidPackageState>> {
+
+        private final List<UidPackageState> mToMatch;
+
+        public PackageStateListMatcher(List<UidPackageState> toMatch) {
+            mToMatch = Objects.requireNonNull(toMatch);
+        }
+
+        @Override
+        public boolean matches(List<UidPackageState> other) {
+            if (other == null) return false;
+            if (other.size() != mToMatch.size()) return false;
+            for (int i = 0; i < mToMatch.size(); i++) {
+                var matcher = new UidPackageStateMatcher(mToMatch.get(i));
+                if (!matcher.matches(other.get(i))) return false;
+            }
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return "Matcher for List<UidState> with uid: " + mToMatch;
+        }
+    }
+
+    @Before
+    public void setup() {
+        when(mMockPackageStateOne_10000_one.getAppId()).thenReturn(10000);
+        when(mMockPackageStateOne_10000_one.getPackageName()).thenReturn("com.package.one");
+
+        when(mMockPackageStateTwo_10001_two.getAppId()).thenReturn(10001);
+        when(mMockPackageStateTwo_10001_two.getPackageName()).thenReturn("com.package.two");
+
+        // Same state as the first is intentional, emulating multi-user
+        when(mMockPackageStateThree_10000_one.getAppId()).thenReturn(10000);
+        when(mMockPackageStateThree_10000_one.getPackageName()).thenReturn("com.package.one");
+
+        when(mMockPackageStateFour_10000_three.getAppId()).thenReturn(10000);
+        when(mMockPackageStateFour_10000_three.getPackageName()).thenReturn("com.package.three");
+
+        when(mMockPackageStateFive_10001_four.getAppId()).thenReturn(10001);
+        when(mMockPackageStateFive_10001_four.getPackageName()).thenReturn("com.package.four");
+
+        when(mMockPackageStateSix_10000_two.getAppId()).thenReturn(10000);
+        when(mMockPackageStateSix_10000_two.getPackageName()).thenReturn("com.package.two");
+    }
+
+    @Test
+    public void testInitialPackagePopulation() throws Exception {
+        var initPackageListData =
+                List.of(
+                        mMockPackageStateOne_10000_one,
+                        mMockPackageStateTwo_10001_two,
+                        mMockPackageStateThree_10000_one,
+                        mMockPackageStateFour_10000_three,
+                        mMockPackageStateFive_10001_four,
+                        mMockPackageStateSix_10000_two);
+        var expectedPackageList =
+                List.of(
+                        createUidPackageState(
+                                10000,
+                                List.of("com.package.one", "com.package.two", "com.package.three")),
+                        createUidPackageState(
+                                10001, List.of("com.package.two", "com.package.four")));
+
+        mPermissionProvider = new AudioServerPermissionProvider(initPackageListData);
+        mPermissionProvider.onServiceStart(mMockPc);
+        verify(mMockPc)
+                .populatePackagesForUids(argThat(new PackageStateListMatcher(expectedPackageList)));
+    }
+
+    @Test
+    public void testOnModifyPackageState_whenNewUid() throws Exception {
+        // 10000: one | 10001: two
+        var initPackageListData =
+                List.of(mMockPackageStateOne_10000_one, mMockPackageStateTwo_10001_two);
+        mPermissionProvider = new AudioServerPermissionProvider(initPackageListData);
+        mPermissionProvider.onServiceStart(mMockPc);
+
+        // new uid, including user component
+        mPermissionProvider.onModifyPackageState(1_10002, "com.package.new", false /* isRemove */);
+
+        verify(mMockPc)
+                .updatePackagesForUid(
+                        argThat(new UidPackageStateMatcher(10002, List.of("com.package.new"))));
+        verify(mMockPc).updatePackagesForUid(any()); // exactly once
+    }
+
+    @Test
+    public void testOnModifyPackageState_whenRemoveUid() throws Exception {
+        // 10000: one | 10001: two
+        var initPackageListData =
+                List.of(mMockPackageStateOne_10000_one, mMockPackageStateTwo_10001_two);
+        mPermissionProvider = new AudioServerPermissionProvider(initPackageListData);
+        mPermissionProvider.onServiceStart(mMockPc);
+
+        // Includes user-id
+        mPermissionProvider.onModifyPackageState(1_10000, "com.package.one", true /* isRemove */);
+
+        verify(mMockPc).updatePackagesForUid(argThat(new UidPackageStateMatcher(10000, List.of())));
+        verify(mMockPc).updatePackagesForUid(any()); // exactly once
+    }
+
+    @Test
+    public void testOnModifyPackageState_whenUpdatedUidAddition() throws Exception {
+        // 10000: one | 10001: two
+        var initPackageListData =
+                List.of(mMockPackageStateOne_10000_one, mMockPackageStateTwo_10001_two);
+        mPermissionProvider = new AudioServerPermissionProvider(initPackageListData);
+        mPermissionProvider.onServiceStart(mMockPc);
+
+        // Includes user-id
+        mPermissionProvider.onModifyPackageState(1_10000, "com.package.new", false /* isRemove */);
+
+        verify(mMockPc)
+                .updatePackagesForUid(
+                        argThat(
+                                new UidPackageStateMatcher(
+                                        10000, List.of("com.package.one", "com.package.new"))));
+        verify(mMockPc).updatePackagesForUid(any()); // exactly once
+    }
+
+    @Test
+    public void testOnModifyPackageState_whenUpdateUidRemoval() throws Exception {
+        // 10000: one, two | 10001: two
+        var initPackageListData =
+                List.of(
+                        mMockPackageStateOne_10000_one,
+                        mMockPackageStateTwo_10001_two,
+                        mMockPackageStateSix_10000_two);
+        mPermissionProvider = new AudioServerPermissionProvider(initPackageListData);
+        mPermissionProvider.onServiceStart(mMockPc);
+
+        // Includes user-id
+        mPermissionProvider.onModifyPackageState(1_10000, "com.package.one", true /* isRemove */);
+
+        verify(mMockPc)
+                .updatePackagesForUid(
+                        argThat(
+                                new UidPackageStateMatcher(
+                                        createUidPackageState(10000, List.of("com.package.two")))));
+        verify(mMockPc).updatePackagesForUid(any()); // exactly once
+    }
+
+    @Test
+    public void testOnServiceStart() throws Exception {
+        // 10000: one, two | 10001: two
+        var initPackageListData =
+                List.of(
+                        mMockPackageStateOne_10000_one,
+                        mMockPackageStateTwo_10001_two,
+                        mMockPackageStateSix_10000_two);
+        mPermissionProvider = new AudioServerPermissionProvider(initPackageListData);
+        mPermissionProvider.onServiceStart(mMockPc);
+        mPermissionProvider.onModifyPackageState(1_10000, "com.package.one", true /* isRemove */);
+        verify(mMockPc)
+                .updatePackagesForUid(
+                        argThat(new UidPackageStateMatcher(10000, List.of("com.package.two"))));
+
+        verify(mMockPc).updatePackagesForUid(any()); // exactly once
+        mPermissionProvider.onModifyPackageState(
+                1_10000, "com.package.three", false /* isRemove */);
+        verify(mMockPc)
+                .updatePackagesForUid(
+                        argThat(
+                                new UidPackageStateMatcher(
+                                        10000, List.of("com.package.two", "com.package.three"))));
+        verify(mMockPc, times(2)).updatePackagesForUid(any()); // exactly twice
+        // state is now 10000: two, three | 10001: two
+
+        // simulate restart of the service
+        mPermissionProvider.onServiceStart(null); // should handle null
+        var newMockPc = mock(INativePermissionController.class);
+        mPermissionProvider.onServiceStart(newMockPc);
+
+        var expectedPackageList =
+                List.of(
+                        createUidPackageState(
+                                10000, List.of("com.package.two", "com.package.three")),
+                        createUidPackageState(10001, List.of("com.package.two")));
+
+        verify(newMockPc)
+                .populatePackagesForUids(argThat(new PackageStateListMatcher(expectedPackageList)));
+
+        verify(newMockPc, never()).updatePackagesForUid(any());
+        // updates should still work after restart
+        mPermissionProvider.onModifyPackageState(10001, "com.package.four", false /* isRemove */);
+        verify(newMockPc)
+                .updatePackagesForUid(
+                        argThat(
+                                new UidPackageStateMatcher(
+                                        10001, List.of("com.package.two", "com.package.four"))));
+        // exactly once
+        verify(newMockPc).updatePackagesForUid(any());
+    }
+
+    private static UidPackageState createUidPackageState(int uid, List<String> packages) {
+        var res = new UidPackageState();
+        res.uid = uid;
+        res.packageNames = packages;
+        return res;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
index 634877e..037c3c0 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
@@ -66,6 +66,7 @@
     @Mock private AppOpsManager mMockAppOpsManager;
     @Mock private AudioPolicyFacade mMockAudioPolicy;
     @Mock private PermissionEnforcer mMockPermissionEnforcer;
+    @Mock private AudioServerPermissionProvider mMockPermissionProvider;
 
     // the class being unit-tested here
     private AudioService mAudioService;
@@ -86,7 +87,7 @@
                 .thenReturn(AppOpsManager.MODE_ALLOWED);
         mAudioService = new AudioService(mContext, mSpyAudioSystem, mSpySystemServer,
                 mSettingsAdapter, mAudioVolumeGroupHelper, mMockAudioPolicy, null,
-                mMockAppOpsManager, mMockPermissionEnforcer);
+                mMockAppOpsManager, mMockPermissionEnforcer, mMockPermissionProvider);
     }
 
     /**
diff --git a/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java
index 8dfcc18..27b552f 100644
--- a/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java
@@ -22,11 +22,13 @@
 import static org.mockito.Mockito.mock;
 
 import android.annotation.NonNull;
+import android.app.AppOpsManager;
 import android.content.Context;
 import android.media.AudioDeviceAttributes;
 import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
 import android.media.IDeviceVolumeBehaviorDispatcher;
+import android.os.PermissionEnforcer;
 import android.os.test.TestLooper;
 import android.platform.test.annotations.Presubmit;
 
@@ -75,7 +77,8 @@
         mAudioVolumeGroupHelper = new AudioVolumeGroupHelperBase();
         mAudioService = new AudioService(mContext, mAudioSystem, mSystemServer,
                 mSettingsAdapter, mAudioVolumeGroupHelper, mAudioPolicyMock,
-                mTestLooper.getLooper());
+                mTestLooper.getLooper(), mock(AppOpsManager.class), mock(PermissionEnforcer.class),
+                mock(AudioServerPermissionProvider.class));
         mTestLooper.dispatchAll();
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/audio/ServiceHolderTest.java b/services/tests/servicestests/src/com/android/server/audio/ServiceHolderTest.java
new file mode 100644
index 0000000..39f19ae
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/audio/ServiceHolderTest.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.audio;
+
+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.any;
+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.media.IAudioPolicyService;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.IServiceCallback;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+import java.util.function.Function;
+
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public class ServiceHolderTest {
+
+    private static final String AUDIO_POLICY_SERVICE_NAME = "media.audio_policy";
+
+    @Rule
+    public final MockitoRule mockito = MockitoJUnit.rule();
+
+    // the actual class under test
+    private ServiceHolder<IAudioPolicyService> mServiceHolder;
+
+    @Mock private ServiceHolder.ServiceProviderFacade mServiceProviderFacade;
+
+    @Mock private IAudioPolicyService mAudioPolicyService;
+
+    @Mock private IBinder mBinder;
+
+    @Mock private Consumer<IAudioPolicyService> mTaskOne;
+    @Mock private Consumer<IAudioPolicyService> mTaskTwo;
+
+    @Before
+    public void setUp() throws Exception {
+        mServiceHolder =
+                new ServiceHolder(
+                        AUDIO_POLICY_SERVICE_NAME,
+                        (Function<IBinder, IAudioPolicyService>)
+                                binder -> {
+                                    if (binder == mBinder) {
+                                        return mAudioPolicyService;
+                                    } else {
+                                        return mock(IAudioPolicyService.class);
+                                    }
+                                },
+                        r -> r.run(),
+                        mServiceProviderFacade);
+        when(mAudioPolicyService.asBinder()).thenReturn(mBinder);
+    }
+
+    @Test
+    public void testListenerRegistered_whenConstructed() {
+        verify(mServiceProviderFacade)
+                .registerForNotifications(eq(AUDIO_POLICY_SERVICE_NAME), ArgumentMatchers.any());
+    }
+
+    @Test
+    public void testServiceSuccessfullyPopulated_whenCallback() throws RemoteException {
+        initializeViaCallback();
+        verify(mBinder).linkToDeath(any(), anyInt());
+        assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService);
+    }
+
+    @Test
+    public void testCheckServiceCalled_whenUncached() {
+        when(mServiceProviderFacade.checkService(eq(AUDIO_POLICY_SERVICE_NAME)))
+                .thenReturn(mBinder);
+        assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService);
+    }
+
+    @Test
+    public void testCheckServiceTransmitsNull() {
+        assertThat(mServiceHolder.checkService()).isEqualTo(null);
+    }
+
+    @Test
+    public void testWaitForServiceCalled_whenUncached() {
+        when(mServiceProviderFacade.waitForService(eq(AUDIO_POLICY_SERVICE_NAME)))
+                .thenReturn(mBinder);
+        assertThat(mServiceHolder.waitForService()).isEqualTo(mAudioPolicyService);
+    }
+
+    @Test
+    public void testCheckServiceNotCalled_whenCached() {
+        initializeViaCallback();
+        mServiceHolder.checkService();
+        verify(mServiceProviderFacade, never()).checkService(any());
+    }
+
+    @Test
+    public void testWaitForServiceNotCalled_whenCached() {
+        initializeViaCallback();
+        mServiceHolder.waitForService();
+        verify(mServiceProviderFacade, never()).waitForService(any());
+    }
+
+    @Test
+    public void testStartTaskCalled_onStart() {
+        mServiceHolder.registerOnStartTask(mTaskOne);
+        mServiceHolder.registerOnStartTask(mTaskTwo);
+        mServiceHolder.unregisterOnStartTask(mTaskOne);
+        when(mServiceProviderFacade.checkService(eq(AUDIO_POLICY_SERVICE_NAME)))
+                .thenReturn(mBinder);
+
+        assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService);
+
+        verify(mTaskTwo).accept(eq(mAudioPolicyService));
+        verify(mTaskOne, never()).accept(any());
+    }
+
+    @Test
+    public void testStartTaskCalled_onStartFromCallback() {
+        mServiceHolder.registerOnStartTask(mTaskOne);
+        mServiceHolder.registerOnStartTask(mTaskTwo);
+        mServiceHolder.unregisterOnStartTask(mTaskOne);
+
+        initializeViaCallback();
+
+        assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService);
+        verify(mTaskTwo).accept(eq(mAudioPolicyService));
+        verify(mTaskOne, never()).accept(any());
+    }
+
+    @Test
+    public void testStartTaskCalled_onRegisterAfterStarted() {
+        initializeViaCallback();
+        mServiceHolder.registerOnStartTask(mTaskOne);
+        verify(mTaskOne).accept(eq(mAudioPolicyService));
+    }
+
+    @Test
+    public void testBinderDied_clearsServiceAndUnlinks() {
+        initializeViaCallback();
+        assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService);
+
+        mServiceHolder.binderDied(mBinder);
+
+        verify(mBinder).unlinkToDeath(any(), anyInt());
+        assertThat(mServiceHolder.checkService()).isEqualTo(null);
+        verify(mServiceProviderFacade).checkService(eq(AUDIO_POLICY_SERVICE_NAME));
+    }
+
+    @Test
+    public void testBinderDied_callsDeathTasks() {
+        mServiceHolder.registerOnDeathTask(mTaskOne);
+        mServiceHolder.registerOnDeathTask(mTaskTwo);
+        initializeViaCallback();
+        assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService);
+        mServiceHolder.unregisterOnDeathTask(mTaskOne);
+
+        mServiceHolder.binderDied(mBinder);
+
+        verify(mTaskTwo).accept(eq(mAudioPolicyService));
+        verify(mTaskOne, never()).accept(any());
+    }
+
+    @Test
+    public void testAttemptClear_clearsServiceAndUnlinks() {
+        initializeViaCallback();
+        assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService);
+
+        mServiceHolder.attemptClear(mBinder);
+
+        verify(mBinder).unlinkToDeath(any(), anyInt());
+        assertThat(mServiceHolder.checkService()).isEqualTo(null);
+        verify(mServiceProviderFacade).checkService(eq(AUDIO_POLICY_SERVICE_NAME));
+    }
+
+    @Test
+    public void testAttemptClear_callsDeathTasks() {
+        mServiceHolder.registerOnDeathTask(mTaskOne);
+        mServiceHolder.registerOnDeathTask(mTaskTwo);
+        initializeViaCallback();
+        assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService);
+        mServiceHolder.unregisterOnDeathTask(mTaskOne);
+
+        mServiceHolder.attemptClear(mBinder);
+
+        verify(mTaskTwo).accept(eq(mAudioPolicyService));
+        verify(mTaskOne, never()).accept(any());
+    }
+
+    @Test
+    public void testSet_whenServiceSet_isIgnored() {
+        mServiceHolder.registerOnStartTask(mTaskOne);
+        when(mServiceProviderFacade.checkService(eq(AUDIO_POLICY_SERVICE_NAME)))
+                .thenReturn(mBinder);
+        assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService);
+
+        verify(mTaskOne).accept(eq(mAudioPolicyService));
+
+        // get the callback
+        ArgumentCaptor<IServiceCallback> cb = ArgumentCaptor.forClass(IServiceCallback.class);
+        verify(mServiceProviderFacade)
+                .registerForNotifications(eq(AUDIO_POLICY_SERVICE_NAME), cb.capture());
+
+        // Simulate a service callback with a different instance
+        try {
+            cb.getValue().onRegistration(AUDIO_POLICY_SERVICE_NAME, new Binder());
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+
+        // No additional start task call (i.e. only the first verify)
+        verify(mTaskOne).accept(any());
+        // Same instance
+        assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService);
+
+    }
+
+    @Test
+    public void testClear_whenServiceCleared_isIgnored() {
+        mServiceHolder.registerOnDeathTask(mTaskOne);
+        mServiceHolder.attemptClear(mBinder);
+        verify(mTaskOne, never()).accept(any());
+    }
+
+    @Test
+    public void testClear_withDifferentCookie_isIgnored() {
+        mServiceHolder.registerOnDeathTask(mTaskOne);
+        initializeViaCallback();
+        assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService);
+
+        // Notif for stale cookie
+        mServiceHolder.attemptClear(new Binder());
+
+        // Service shouldn't be cleared
+        assertThat(mServiceHolder.checkService()).isEqualTo(mAudioPolicyService);
+        // No death tasks should fire
+        verify(mTaskOne, never()).accept(any());
+    }
+
+    private void initializeViaCallback() {
+        ArgumentCaptor<IServiceCallback> cb = ArgumentCaptor.forClass(IServiceCallback.class);
+        verify(mServiceProviderFacade)
+                .registerForNotifications(eq(AUDIO_POLICY_SERVICE_NAME), cb.capture());
+
+        try {
+            cb.getValue().onRegistration(AUDIO_POLICY_SERVICE_NAME, mBinder);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
index 23728db..8e34ee1 100644
--- a/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
@@ -40,6 +40,7 @@
 import static android.view.KeyEvent.KEYCODE_VOLUME_UP;
 
 import static com.android.media.audio.Flags.FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME;
+import static com.android.media.audio.Flags.FLAG_ABS_VOLUME_INDEX_FIX;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -132,9 +133,12 @@
     @Mock
     private PermissionEnforcer mMockPermissionEnforcer;
     @Mock
+    private AudioServerPermissionProvider mMockPermissionProvider;
+    @Mock
     private AudioVolumeGroupHelperBase mAudioVolumeGroupHelper;
 
-    private final AudioPolicyFacade mFakeAudioPolicy = lookbackAudio -> false;
+    @Mock
+    private AudioPolicyFacade mMockAudioPolicy;
 
     private AudioVolumeGroup mAudioMusicVolumeGroup;
 
@@ -153,9 +157,10 @@
                 SystemServerAdapter systemServer, SettingsAdapter settings,
                 AudioVolumeGroupHelperBase audioVolumeGroupHelper, AudioPolicyFacade audioPolicy,
                 @Nullable Looper looper, AppOpsManager appOps,
-                @NonNull PermissionEnforcer enforcer) {
+                @NonNull PermissionEnforcer enforcer,
+                AudioServerPermissionProvider permissionProvider) {
             super(context, audioSystem, systemServer, settings, audioVolumeGroupHelper,
-                    audioPolicy, looper, appOps, enforcer);
+                    audioPolicy, looper, appOps, enforcer, permissionProvider);
         }
 
         public void setDeviceForStream(int stream, int device) {
@@ -209,8 +214,9 @@
         mAm = mContext.getSystemService(AudioManager.class);
 
         mAudioService = new MyAudioService(mContext, mSpyAudioSystem, mSpySystemServer,
-                mSettingsAdapter, mAudioVolumeGroupHelper, mFakeAudioPolicy,
-                mTestLooper.getLooper(), mMockAppOpsManager, mMockPermissionEnforcer);
+                mSettingsAdapter, mAudioVolumeGroupHelper, mMockAudioPolicy,
+                mTestLooper.getLooper(), mMockAppOpsManager, mMockPermissionEnforcer,
+                mMockPermissionProvider);
 
         mTestLooper.dispatchAll();
         prepareAudioServiceState();
@@ -552,7 +558,7 @@
     }
 
     @Test
-    @RequiresFlagsDisabled(FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME)
+    @RequiresFlagsDisabled({FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME, FLAG_ABS_VOLUME_INDEX_FIX})
     public void configurablePreScaleAbsoluteVolume_checkIndex() throws Exception {
         final int minIndex = mAm.getStreamMinVolume(STREAM_MUSIC);
         final int maxIndex = mAm.getStreamMaxVolume(STREAM_MUSIC);
@@ -607,6 +613,7 @@
 
     @Test
     @RequiresFlagsEnabled(FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME)
+    @RequiresFlagsDisabled(FLAG_ABS_VOLUME_INDEX_FIX)
     public void disablePreScaleAbsoluteVolume_checkIndex() throws Exception {
         final int minIndex = mAm.getStreamMinVolume(STREAM_MUSIC);
         final int maxIndex = mAm.getStreamMaxVolume(STREAM_MUSIC);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
index 98e119c..473d1dc 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java
@@ -139,12 +139,13 @@
 
     @Test
     public void isValid_setSystemAudioMode() {
-        assertMessageValidity("40:72:00").isEqualTo(OK);
-        assertMessageValidity("4F:72:01:03").isEqualTo(OK);
+        assertMessageValidity("50:72:00").isEqualTo(OK);
+        assertMessageValidity("50:72:01").isEqualTo(OK);
+        assertMessageValidity("5F:72:01:03").isEqualTo(ERROR_PARAMETER_LONG);
 
-        assertMessageValidity("F0:72").isEqualTo(ERROR_SOURCE);
-        assertMessageValidity("40:72").isEqualTo(ERROR_PARAMETER_SHORT);
-        assertMessageValidity("40:72:02").isEqualTo(ERROR_PARAMETER);
+        assertMessageValidity("40:72:00").isEqualTo(ERROR_SOURCE);
+        assertMessageValidity("50:72").isEqualTo(ERROR_PARAMETER_SHORT);
+        assertMessageValidity("50:72:02").isEqualTo(ERROR_PARAMETER);
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
index c89c32a..74583dd 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiUtilsTest.java
@@ -709,4 +709,18 @@
         assertThat(HdmiUtils.buildMessage("40:32:65:6E:67").getParams()).isEqualTo(
                 new byte[]{0x65, 0x6E, 0x67});
     }
+
+    @Test
+    public void testVerifyAddressType() {
+        assertTrue(HdmiUtils.verifyAddressType(Constants.ADDR_TV,
+                HdmiDeviceInfo.DEVICE_TV));
+        assertTrue(HdmiUtils.verifyAddressType(Constants.ADDR_AUDIO_SYSTEM,
+                HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM));
+        assertTrue(HdmiUtils.verifyAddressType(Constants.ADDR_PLAYBACK_1,
+                HdmiDeviceInfo.DEVICE_PLAYBACK));
+        assertFalse(HdmiUtils.verifyAddressType(Constants.ADDR_SPECIFIC_USE,
+                HdmiDeviceInfo.DEVICE_AUDIO_SYSTEM));
+        assertFalse(HdmiUtils.verifyAddressType(Constants.ADDR_PLAYBACK_2,
+                HdmiDeviceInfo.DEVICE_VIDEO_PROCESSOR));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
index 7dd1847..50cfa75 100644
--- a/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
+++ b/services/tests/servicestests/src/com/android/server/locales/LocaleManagerBackupRestoreTest.java
@@ -20,7 +20,6 @@
 import static junit.framework.Assert.assertNull;
 import static junit.framework.Assert.assertTrue;
 
-import static org.junit.Assert.assertNotNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -52,6 +51,7 @@
 import android.util.SparseArray;
 import android.util.Xml;
 
+import androidx.test.core.app.ApplicationProvider;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.internal.content.PackageMonitor;
@@ -70,6 +70,7 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
@@ -95,21 +96,21 @@
     private static final int DEFAULT_USER_ID = 0;
     private static final int WORK_PROFILE_USER_ID = 10;
     private static final int DEFAULT_UID = Binder.getCallingUid() + 100;
+    private static final int WORK_PROFILE_UID = Binder.getCallingUid() + 1000100;
     private static final long DEFAULT_CREATION_TIME_MILLIS = 1000;
     private static final Duration RETENTION_PERIOD = Duration.ofDays(3);
     private static final LocaleList DEFAULT_LOCALES =
             LocaleList.forLanguageTags(DEFAULT_LOCALE_TAGS);
     private static final Map<String, LocalesInfo> DEFAULT_PACKAGE_LOCALES_INFO_MAP = Map.of(
             DEFAULT_PACKAGE_NAME, new LocalesInfo(DEFAULT_LOCALE_TAGS, false));
-    private static final SparseArray<LocaleManagerBackupHelper.StagedData> STAGE_DATA =
-            new SparseArray<>();
+    private final SparseArray<File> mStagedDataFiles = new SparseArray<>();
+    private File mArchivedPackageFile;
 
     private LocaleManagerBackupHelper mBackupHelper;
     private long mCurrentTimeMillis;
+    private Context mContext = spy(ApplicationProvider.getApplicationContext());
 
     @Mock
-    private Context mMockContext;
-    @Mock
     private PackageManager mMockPackageManager;
     @Mock
     private LocaleManagerService mMockLocaleManagerService;
@@ -138,23 +139,28 @@
 
     @Before
     public void setUp() throws Exception {
-        mMockContext = mock(Context.class);
         mMockPackageManager = mock(PackageManager.class);
         mMockLocaleManagerService = mock(LocaleManagerService.class);
         mMockDelegateAppLocalePackages = mock(SharedPreferences.class);
         mMockSpEditor = mock(SharedPreferences.Editor.class);
         SystemAppUpdateTracker systemAppUpdateTracker = mock(SystemAppUpdateTracker.class);
 
-        doReturn(mMockPackageManager).when(mMockContext).getPackageManager();
+        doReturn(mMockPackageManager).when(mContext).getPackageManager();
         doReturn(mMockSpEditor).when(mMockDelegateAppLocalePackages).edit();
 
         HandlerThread broadcastHandlerThread = new HandlerThread(TAG,
                 Process.THREAD_PRIORITY_BACKGROUND);
         broadcastHandlerThread.start();
 
-        mBackupHelper = spy(new ShadowLocaleManagerBackupHelper(mMockContext,
-                mMockLocaleManagerService, mMockPackageManager, mClock, STAGE_DATA,
-                broadcastHandlerThread, mMockDelegateAppLocalePackages));
+        File file0 = new File(mContext.getCacheDir(), "file_user_0.txt");
+        File file10 = new File(mContext.getCacheDir(), "file_user_10.txt");
+        mStagedDataFiles.put(DEFAULT_USER_ID, file0);
+        mStagedDataFiles.put(WORK_PROFILE_USER_ID, file10);
+        mArchivedPackageFile = new File(mContext.getCacheDir(), "file_archived.txt");
+
+        mBackupHelper = spy(new ShadowLocaleManagerBackupHelper(mContext,
+            mMockLocaleManagerService, mMockPackageManager, mClock, broadcastHandlerThread,
+                mStagedDataFiles, mArchivedPackageFile, mMockDelegateAppLocalePackages));
         doNothing().when(mBackupHelper).notifyBackupManager();
 
         mUserMonitor = mBackupHelper.getUserMonitor();
@@ -165,7 +171,16 @@
 
     @After
     public void tearDown() throws Exception {
-        STAGE_DATA.clear();
+        for (int i = 0; i < mStagedDataFiles.size(); i++) {
+            int userId = mStagedDataFiles.keyAt(i);
+            File file = mStagedDataFiles.get(userId);
+            SharedPreferences sp = mContext.getSharedPreferences(file, Context.MODE_PRIVATE);
+            sp.edit().clear().commit();
+            if (file.exists()) {
+                file.delete();
+            }
+        }
+        mStagedDataFiles.clear();
     }
 
     @Test
@@ -543,17 +558,21 @@
         mPackageMonitor.onPackageAddedWithExtras(pkgNameA, DEFAULT_UID, bundle);
         mPackageMonitor.onPackageAddedWithExtras(pkgNameB, DEFAULT_UID, bundle);
 
+        checkArchivedFileExists();
+
         mBackupHelper.stageAndApplyRestoredPayload(out.toByteArray(), DEFAULT_USER_ID);
 
         verifyNothingRestored();
 
         setUpPackageInstalled(pkgNameA);
 
-        mPackageMonitor.onPackageUpdateFinished(pkgNameA, DEFAULT_UID);
+        mBackupHelper.onPackageUpdateFinished(pkgNameA, DEFAULT_UID);
 
         verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameA, DEFAULT_USER_ID,
                 LocaleList.forLanguageTags(langTagsA), false, FrameworkStatsLog
                 .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_BACKUP_RESTORE);
+        checkArchivedFileExists();
+
 
         mBackupHelper.persistLocalesModificationInfo(DEFAULT_USER_ID, pkgNameA, false, false);
 
@@ -565,11 +584,12 @@
 
         setUpPackageInstalled(pkgNameB);
 
-        mPackageMonitor.onPackageUpdateFinished(pkgNameB, DEFAULT_UID);
+        mBackupHelper.onPackageUpdateFinished(pkgNameB, DEFAULT_UID);
 
         verify(mMockLocaleManagerService, times(1)).setApplicationLocales(pkgNameB, DEFAULT_USER_ID,
                 LocaleList.forLanguageTags(langTagsB), true, FrameworkStatsLog
                 .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_BACKUP_RESTORE);
+        checkArchivedFileDoesNotExist();
 
         mBackupHelper.persistLocalesModificationInfo(DEFAULT_USER_ID, pkgNameB, true, false);
 
@@ -723,7 +743,7 @@
         Intent intent = new Intent();
         intent.setAction(Intent.ACTION_USER_REMOVED);
         intent.putExtra(Intent.EXTRA_USER_HANDLE, DEFAULT_USER_ID);
-        mUserMonitor.onReceive(mMockContext, intent);
+        mUserMonitor.onReceive(mContext, intent);
 
         // Stage data should be removed only for DEFAULT_USER_ID.
         checkStageDataDoesNotExist(DEFAULT_USER_ID);
@@ -732,6 +752,72 @@
     }
 
     @Test
+    public void testRestore_multipleProfile_restoresFromStage_ArchiveEnabled() throws Exception {
+        final ByteArrayOutputStream outDefault = new ByteArrayOutputStream();
+        writeTestPayload(outDefault, DEFAULT_PACKAGE_LOCALES_INFO_MAP);
+        final ByteArrayOutputStream outWorkProfile = new ByteArrayOutputStream();
+        String anotherPackage = "com.android.anotherapp";
+        String anotherLangTags = "mr,zh";
+        LocalesInfo localesInfo = new LocalesInfo(anotherLangTags, true);
+        HashMap<String, LocalesInfo> pkgLocalesMapWorkProfile = new HashMap<>();
+        pkgLocalesMapWorkProfile.put(anotherPackage, localesInfo);
+        writeTestPayload(outWorkProfile, pkgLocalesMapWorkProfile);
+        // DEFAULT_PACKAGE_NAME is NOT installed on the device.
+        setUpPackageNotInstalled(DEFAULT_PACKAGE_NAME);
+        setUpPackageNotInstalled(anotherPackage);
+        setUpLocalesForPackage(DEFAULT_PACKAGE_NAME, LocaleList.getEmptyLocaleList());
+        setUpLocalesForPackage(anotherPackage, LocaleList.getEmptyLocaleList());
+        setUpPackageNamesForSp(new ArraySet<>());
+
+        Bundle bundle = new Bundle();
+        bundle.putBoolean(Intent.EXTRA_ARCHIVAL, true);
+        mPackageMonitor.onPackageAddedWithExtras(DEFAULT_PACKAGE_NAME, DEFAULT_UID, bundle);
+        mPackageMonitor.onPackageAddedWithExtras(anotherPackage, WORK_PROFILE_UID, bundle);
+
+        checkArchivedFileExists();
+
+        mBackupHelper.stageAndApplyRestoredPayload(outDefault.toByteArray(), DEFAULT_USER_ID);
+        mBackupHelper.stageAndApplyRestoredPayload(outWorkProfile.toByteArray(),
+                WORK_PROFILE_USER_ID);
+
+        verifyNothingRestored();
+        verifyStageDataForUser(DEFAULT_PACKAGE_LOCALES_INFO_MAP,
+                DEFAULT_CREATION_TIME_MILLIS, DEFAULT_USER_ID);
+        verifyStageDataForUser(pkgLocalesMapWorkProfile,
+                DEFAULT_CREATION_TIME_MILLIS, WORK_PROFILE_USER_ID);
+
+        setUpPackageInstalled(DEFAULT_PACKAGE_NAME);
+        mBackupHelper.onPackageUpdateFinished(DEFAULT_PACKAGE_NAME, DEFAULT_UID);
+
+        verify(mMockLocaleManagerService, times(1)).setApplicationLocales(DEFAULT_PACKAGE_NAME,
+                DEFAULT_USER_ID,
+                LocaleList.forLanguageTags(DEFAULT_LOCALE_TAGS), false, FrameworkStatsLog
+                        .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_BACKUP_RESTORE);
+        checkArchivedFileExists();
+        checkStageDataDoesNotExist(DEFAULT_USER_ID);
+
+        mBackupHelper.persistLocalesModificationInfo(DEFAULT_USER_ID, DEFAULT_PACKAGE_NAME, false,
+                false);
+
+        verify(mMockSpEditor, times(0)).putStringSet(anyString(), any());
+
+        setUpPackageInstalled(anotherPackage);
+        mBackupHelper.onPackageUpdateFinished(anotherPackage, WORK_PROFILE_UID);
+
+        verify(mMockLocaleManagerService, times(1)).setApplicationLocales(anotherPackage,
+                WORK_PROFILE_USER_ID,
+                LocaleList.forLanguageTags(anotherLangTags), true, FrameworkStatsLog
+                        .APPLICATION_LOCALES_CHANGED__CALLER__CALLER_BACKUP_RESTORE);
+        checkArchivedFileDoesNotExist();
+
+        mBackupHelper.persistLocalesModificationInfo(DEFAULT_USER_ID, anotherPackage, true, false);
+
+        verify(mMockSpEditor, times(1)).putStringSet(Integer.toString(DEFAULT_USER_ID),
+            new ArraySet<>(Arrays.asList(anotherPackage)));
+        checkStageDataDoesNotExist(WORK_PROFILE_USER_ID);
+    }
+
+    @Test
     public void testPackageRemoved_noInfoInSp() throws Exception {
         String pkgNameA = "com.android.myAppA";
         String pkgNameB = "com.android.myAppB";
@@ -858,10 +944,22 @@
 
     private void verifyStageDataForUser(Map<String, LocalesInfo> expectedPkgLocalesMap,
             long expectedCreationTimeMillis, int userId) {
-        LocaleManagerBackupHelper.StagedData stagedDataForUser = STAGE_DATA.get(userId);
-        assertNotNull(stagedDataForUser);
-        assertEquals(expectedCreationTimeMillis, stagedDataForUser.mCreationTimeMillis);
-        verifyStageData(expectedPkgLocalesMap, stagedDataForUser.mPackageStates);
+        SharedPreferences sp = mContext.getSharedPreferences(mStagedDataFiles.get(userId),
+                Context.MODE_PRIVATE);
+        assertTrue(sp.getAll().size() > 0);
+        assertEquals(expectedCreationTimeMillis, sp.getLong("staged_data_time", -1));
+        verifyStageData(expectedPkgLocalesMap, sp);
+    }
+
+    private static void verifyStageData(Map<String, LocalesInfo> expectedPkgLocalesMap,
+            SharedPreferences sp) {
+        for (String pkg : expectedPkgLocalesMap.keySet()) {
+            assertTrue(!sp.getString(pkg, "").isEmpty());
+            String[] info = sp.getString(pkg, "").split(" s:");
+            assertEquals(expectedPkgLocalesMap.get(pkg).mLocales, info[0]);
+            assertEquals(expectedPkgLocalesMap.get(pkg).mSetFromDelegate,
+                    Boolean.parseBoolean(info[1]));
+        }
     }
 
     private static void verifyStageData(Map<String, LocalesInfo> expectedPkgLocalesMap,
@@ -875,11 +973,19 @@
         }
     }
 
-    private static void checkStageDataExists(int userId) {
-        assertNotNull(STAGE_DATA.get(userId));
+    private void checkStageDataExists(int userId) {
+        assertTrue(mStagedDataFiles.get(userId) != null && mStagedDataFiles.get(userId).exists());
     }
 
-    private static void checkStageDataDoesNotExist(int userId) {
-        assertNull(STAGE_DATA.get(userId));
+    private void checkStageDataDoesNotExist(int userId) {
+        assertTrue(mStagedDataFiles.get(userId) == null || !mStagedDataFiles.get(userId).exists());
     }
-}
+
+    private void checkArchivedFileExists() {
+        assertTrue(mArchivedPackageFile.exists());
+    }
+
+    private void checkArchivedFileDoesNotExist() {
+        assertTrue(!mArchivedPackageFile.exists());
+    }
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java b/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java
index 9f7cbe3..b46902d 100644
--- a/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java
+++ b/services/tests/servicestests/src/com/android/server/locales/ShadowLocaleManagerBackupHelper.java
@@ -22,6 +22,7 @@
 import android.os.HandlerThread;
 import android.util.SparseArray;
 
+import java.io.File;
 import java.time.Clock;
 
 /**
@@ -33,9 +34,9 @@
     ShadowLocaleManagerBackupHelper(Context context,
             LocaleManagerService localeManagerService,
             PackageManager packageManager, Clock clock,
-            SparseArray<LocaleManagerBackupHelper.StagedData> stagedData,
-            HandlerThread broadcastHandlerThread, SharedPreferences delegateAppLocalePackages) {
-        super(context, localeManagerService, packageManager, clock, stagedData,
-                broadcastHandlerThread, delegateAppLocalePackages);
+            HandlerThread broadcastHandlerThread, SparseArray<File> stagedDataFiles,
+            File archivedPackagesFile, SharedPreferences delegateAppLocalePackages) {
+        super(context, localeManagerService, packageManager, clock, broadcastHandlerThread,
+                stagedDataFiles, archivedPackagesFile, delegateAppLocalePackages);
     }
 }
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 4b22652..601a016 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -43,6 +43,8 @@
 import android.content.Intent;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.flag.junit.SetFlagsRule;
 import android.service.gatekeeper.GateKeeperResponse;
@@ -483,18 +485,31 @@
         setSecureFrpMode(true);
         try {
             mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID);
-            fail("Password shouldn't be changeable before FRP unlock");
+            fail("Password shouldn't be changeable while FRP is active");
         } catch (SecurityException e) { }
     }
 
     @Test
-    public void testSetCredentialPossibleInSecureFrpModeAfterSuw() throws RemoteException {
+    @DisableFlags(android.security.Flags.FLAG_FRP_ENFORCEMENT)
+    public void testSetCredentialPossibleInSecureFrpModeAfterSuw_FlagOff() throws RemoteException {
         setUserSetupComplete(true);
         setSecureFrpMode(true);
         setCredential(PRIMARY_USER_ID, newPassword("1234"));
     }
 
     @Test
+    @EnableFlags(android.security.Flags.FLAG_FRP_ENFORCEMENT)
+    public void testSetCredentialNotPossibleInSecureFrpModeAfterSuw_FlagOn()
+            throws RemoteException {
+        setUserSetupComplete(true);
+        setSecureFrpMode(true);
+        try {
+            mService.setLockCredential(newPassword("1234"), nonePassword(), PRIMARY_USER_ID);
+            fail("Password shouldn't be changeable after SUW while FRP is active");
+        } catch (SecurityException e) { }
+    }
+
+    @Test
     public void testPasswordHistoryDisabledByDefault() throws Exception {
         final int userId = PRIMARY_USER_ID;
         checkPasswordHistoryLength(userId, 0);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index 64e6236..17b499e 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -19,6 +19,7 @@
 import static android.content.pm.UserInfo.FLAG_FULL;
 import static android.content.pm.UserInfo.FLAG_PRIMARY;
 import static android.content.pm.UserInfo.FLAG_PROFILE;
+import static android.content.pm.UserInfo.NO_PROFILE_GROUP_ID;
 import static android.os.UserHandle.USER_SYSTEM;
 
 import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_ESCROW_NOT_READY;
@@ -32,6 +33,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyByte;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
@@ -65,6 +67,7 @@
 
 import com.android.internal.widget.RebootEscrowListener;
 import com.android.server.locksettings.ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection;
+import com.android.server.pm.UserManagerInternal;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -107,6 +110,7 @@
 
     private Context mContext;
     private UserManager mUserManager;
+    private UserManagerInternal mUserManagerInternal;
     private RebootEscrowManager.Callbacks mCallbacks;
     private IRebootEscrow mRebootEscrow;
     private ResumeOnRebootServiceConnection mServiceConnection;
@@ -126,13 +130,15 @@
         long getCurrentTimeMillis();
 
         void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount,
-                int escrowDurationInSeconds, int vbmetaDigestStatus, int durationSinceBootComplete);
+                          int escrowDurationInSeconds, int vbmetaDigestStatus,
+                          int durationSinceBootComplete);
     }
 
     static class MockInjector extends RebootEscrowManager.Injector {
         private final IRebootEscrow mRebootEscrow;
         private final RebootEscrowProviderInterface mDefaultRebootEscrowProvider;
         private final UserManager mUserManager;
+        private final UserManagerInternal mUserManagerInternal;
         private final MockableRebootEscrowInjected mInjected;
         private final RebootEscrowKeyStoreManager mKeyStoreManager;
         private boolean mServerBased;
@@ -141,12 +147,16 @@
         private Consumer<ConnectivityManager.NetworkCallback> mNetworkConsumer;
         private boolean mWaitForInternet;
 
-        MockInjector(Context context, UserManager userManager,
+        MockInjector(
+                Context context,
+                UserManager userManager,
+                UserManagerInternal userManagerInternal,
                 IRebootEscrow rebootEscrow,
                 RebootEscrowKeyStoreManager keyStoreManager,
                 LockSettingsStorageTestable storage,
                 MockableRebootEscrowInjected injected) {
-            super(context, storage);
+            // TODO: change this
+            super(context, storage, userManagerInternal);
             mRebootEscrow = rebootEscrow;
             mServerBased = false;
             mWaitForInternet = false;
@@ -159,16 +169,20 @@
                     };
             mDefaultRebootEscrowProvider = new RebootEscrowProviderHalImpl(halInjector);
             mUserManager = userManager;
+            mUserManagerInternal = userManagerInternal;
             mKeyStoreManager = keyStoreManager;
             mInjected = injected;
         }
 
-        MockInjector(Context context, UserManager userManager,
+        MockInjector(
+                Context context,
+                UserManager userManager,
+                UserManagerInternal userManagerInternal,
                 ResumeOnRebootServiceConnection serviceConnection,
                 RebootEscrowKeyStoreManager keyStoreManager,
                 LockSettingsStorageTestable storage,
                 MockableRebootEscrowInjected injected) {
-            super(context, storage);
+            super(context, storage, userManagerInternal);
             mRebootEscrow = null;
             mServerBased = true;
             mWaitForInternet = false;
@@ -187,6 +201,7 @@
             mDefaultRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(
                     storage, injector);
             mUserManager = userManager;
+            mUserManagerInternal = userManagerInternal;
             mKeyStoreManager = keyStoreManager;
             mInjected = injected;
         }
@@ -202,6 +217,11 @@
         }
 
         @Override
+        public UserManagerInternal getUserManagerInternal() {
+            return mUserManagerInternal;
+        }
+
+        @Override
         public boolean serverBasedResumeOnReboot() {
             return mServerBased;
         }
@@ -289,8 +309,8 @@
 
         @Override
         public void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount,
-                int escrowDurationInSeconds, int vbmetaDigestStatus,
-                int durationSinceBootComplete) {
+                                 int escrowDurationInSeconds, int vbmetaDigestStatus,
+                                 int durationSinceBootComplete) {
 
             mInjected.reportMetric(success, errorCode, serviceType, attemptCount,
                     escrowDurationInSeconds, vbmetaDigestStatus, durationSinceBootComplete);
@@ -301,6 +321,7 @@
     public void setUp_baseServices() throws Exception {
         mContext = new ContextWrapper(InstrumentationRegistry.getContext());
         mUserManager = mock(UserManager.class);
+        mUserManagerInternal = mock(UserManagerInternal.class);
         mCallbacks = mock(RebootEscrowManager.Callbacks.class);
         mRebootEscrow = mock(IRebootEscrow.class);
         mServiceConnection = mock(ResumeOnRebootServiceConnection.class);
@@ -314,28 +335,43 @@
                 new File(InstrumentationRegistry.getContext().getFilesDir(), "locksettings"));
 
         ArrayList<UserInfo> users = new ArrayList<>();
-        users.add(new UserInfo(PRIMARY_USER_ID, "primary", FLAG_PRIMARY));
-        users.add(new UserInfo(WORK_PROFILE_USER_ID, "work", FLAG_PROFILE));
-        users.add(new UserInfo(NONSECURE_SECONDARY_USER_ID, "non-secure", FLAG_FULL));
-        users.add(new UserInfo(SECURE_SECONDARY_USER_ID, "secure", FLAG_FULL));
+        users.add(createUser(PRIMARY_USER_ID, "primary", FLAG_PRIMARY, PRIMARY_USER_ID));
+        users.add(createUser(WORK_PROFILE_USER_ID, "work", FLAG_PROFILE, PRIMARY_USER_ID));
+        users.add(
+                createUser(
+                        NONSECURE_SECONDARY_USER_ID, "non-secure", FLAG_FULL, NO_PROFILE_GROUP_ID));
+        users.add(createUser(SECURE_SECONDARY_USER_ID, "secure", FLAG_FULL, NO_PROFILE_GROUP_ID));
         when(mUserManager.getUsers()).thenReturn(users);
         when(mCallbacks.isUserSecure(PRIMARY_USER_ID)).thenReturn(true);
         when(mCallbacks.isUserSecure(WORK_PROFILE_USER_ID)).thenReturn(true);
         when(mCallbacks.isUserSecure(NONSECURE_SECONDARY_USER_ID)).thenReturn(false);
         when(mCallbacks.isUserSecure(SECURE_SECONDARY_USER_ID)).thenReturn(true);
         mInjected = mock(MockableRebootEscrowInjected.class);
-        mMockInjector = new MockInjector(mContext, mUserManager, mRebootEscrow,
-                mKeyStoreManager, mStorage, mInjected);
+        mMockInjector =
+                new MockInjector(
+                        mContext,
+                        mUserManager,
+                        mUserManagerInternal,
+                        mRebootEscrow,
+                        mKeyStoreManager,
+                        mStorage,
+                        mInjected);
         HandlerThread thread = new HandlerThread("RebootEscrowManagerTest");
         thread.start();
         mHandler = new Handler(thread.getLooper());
         mService = new RebootEscrowManager(mMockInjector, mCallbacks, mStorage, mHandler);
-
     }
 
     private void setServerBasedRebootEscrowProvider() throws Exception {
-        mMockInjector = new MockInjector(mContext, mUserManager, mServiceConnection,
-                mKeyStoreManager, mStorage, mInjected);
+        mMockInjector =
+                new MockInjector(
+                        mContext,
+                        mUserManager,
+                        mUserManagerInternal,
+                        mServiceConnection,
+                        mKeyStoreManager,
+                        mStorage,
+                        mInjected);
         mService = new RebootEscrowManager(mMockInjector, mCallbacks, mStorage, mHandler);
     }
 
@@ -352,6 +388,12 @@
         waitForHandler();
     }
 
+    private UserInfo createUser(int id, String name, int flag, int profileGroupId) {
+        UserInfo user = new UserInfo(id, name, flag);
+        when(mUserManagerInternal.getProfileParentId(eq(id))).thenReturn(profileGroupId);
+        return user;
+    }
+
     @Test
     public void prepareRebootEscrow_Success() throws Exception {
         RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
@@ -559,6 +601,172 @@
     }
 
     @Test
+    public void loadRebootEscrowDataIfAvailable_noDataPrimaryUser_Failure() throws Exception {
+        setServerBasedRebootEscrowProvider();
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        clearInvocations(mServiceConnection);
+
+        // escrow secondary user, don't escrow primary user
+        callToRebootEscrowIfNeededAndWait(SECURE_SECONDARY_USER_ID);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
+        verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+
+        assertTrue(mStorage.hasRebootEscrow(SECURE_SECONDARY_USER_ID));
+        assertFalse(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+        // pretend reboot happens here
+        when(mInjected.getBootCount()).thenReturn(1);
+        ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+        ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
+        doNothing()
+                .when(mInjected)
+                .reportMetric(
+                        metricsSuccessCaptor.capture(),
+                        metricsErrorCodeCaptor.capture(),
+                        eq(2) /* Server based */,
+                        eq(1) /* attempt count */,
+                        anyInt(),
+                        eq(0) /* vbmeta status */,
+                        anyInt());
+        mService.loadRebootEscrowDataIfAvailable(null);
+        verify(mServiceConnection, never()).unwrap(any(), anyLong());
+        verify(mCallbacks, never()).onRebootEscrowRestored(anyByte(), any(), anyInt());
+        assertFalse(metricsSuccessCaptor.getValue());
+        assertEquals(
+                Integer.valueOf(RebootEscrowManager.ERROR_NO_REBOOT_ESCROW_DATA),
+                metricsErrorCodeCaptor.getValue());
+    }
+
+    @Test
+    public void loadRebootEscrowDataIfAvailable_noDataSecondaryUser_Success() throws Exception {
+        setServerBasedRebootEscrowProvider();
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        clearInvocations(mServiceConnection);
+
+        // Setup work profile with secondary user as parent.
+        ArrayList<UserInfo> users = new ArrayList<>();
+        users.add(createUser(PRIMARY_USER_ID, "primary", FLAG_PRIMARY, NO_PROFILE_GROUP_ID));
+        users.add(createUser(WORK_PROFILE_USER_ID, "work", FLAG_PROFILE, SECURE_SECONDARY_USER_ID));
+        users.add(
+                createUser(
+                        SECURE_SECONDARY_USER_ID, "secure", FLAG_FULL, SECURE_SECONDARY_USER_ID));
+        when(mUserManager.getUsers()).thenReturn(users);
+
+        // escrow primary user and work profile, don't escrow secondary user
+        callToRebootEscrowIfNeededAndWait(PRIMARY_USER_ID);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+        callToRebootEscrowIfNeededAndWait(WORK_PROFILE_USER_ID);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
+        verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+
+        assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
+        assertFalse(mStorage.hasRebootEscrow(SECURE_SECONDARY_USER_ID));
+        assertTrue(mStorage.hasRebootEscrow(WORK_PROFILE_USER_ID));
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+        // pretend reboot happens here
+        when(mInjected.getBootCount()).thenReturn(1);
+        ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+        doNothing()
+                .when(mInjected)
+                .reportMetric(
+                        metricsSuccessCaptor.capture(),
+                        eq(0) /* error code */,
+                        eq(2) /* Server based */,
+                        eq(1) /* attempt count */,
+                        anyInt(),
+                        eq(0) /* vbmeta status */,
+                        anyInt());
+        when(mServiceConnection.unwrap(any(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+
+        mService.loadRebootEscrowDataIfAvailable(null);
+
+        verify(mServiceConnection).unwrap(any(), anyLong());
+        verify(mCallbacks).onRebootEscrowRestored(anyByte(), any(), eq(PRIMARY_USER_ID));
+        verify(mCallbacks, never())
+                .onRebootEscrowRestored(anyByte(), any(), eq(SECURE_SECONDARY_USER_ID));
+        verify(mCallbacks, never())
+                .onRebootEscrowRestored(anyByte(), any(), eq(WORK_PROFILE_USER_ID));
+        verify(mCallbacks, never())
+                .onRebootEscrowRestored(anyByte(), any(), eq(NONSECURE_SECONDARY_USER_ID));
+        assertTrue(metricsSuccessCaptor.getValue());
+    }
+
+    @Test
+    public void loadRebootEscrowDataIfAvailable_noDataWorkProfile_Success() throws Exception {
+        setServerBasedRebootEscrowProvider();
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        clearInvocations(mServiceConnection);
+
+        // escrow primary user and secondary user, don't escrow work profile
+        callToRebootEscrowIfNeededAndWait(PRIMARY_USER_ID);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+        callToRebootEscrowIfNeededAndWait(SECURE_SECONDARY_USER_ID);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
+        verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+
+        assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
+        assertTrue(mStorage.hasRebootEscrow(SECURE_SECONDARY_USER_ID));
+        assertFalse(mStorage.hasRebootEscrow(WORK_PROFILE_USER_ID));
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+        // pretend reboot happens here
+        when(mInjected.getBootCount()).thenReturn(1);
+        ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+        doNothing()
+                .when(mInjected)
+                .reportMetric(
+                        metricsSuccessCaptor.capture(),
+                        eq(0) /* error code */,
+                        eq(2) /* Server based */,
+                        eq(1) /* attempt count */,
+                        anyInt(),
+                        eq(0) /* vbmeta status */,
+                        anyInt());
+        when(mServiceConnection.unwrap(any(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+
+        mService.loadRebootEscrowDataIfAvailable(null);
+
+        verify(mServiceConnection).unwrap(any(), anyLong());
+        verify(mCallbacks).onRebootEscrowRestored(anyByte(), any(), eq(PRIMARY_USER_ID));
+        verify(mCallbacks).onRebootEscrowRestored(anyByte(), any(), eq(SECURE_SECONDARY_USER_ID));
+        verify(mCallbacks, never())
+                .onRebootEscrowRestored(anyByte(), any(), eq(WORK_PROFILE_USER_ID));
+        verify(mCallbacks, never())
+                .onRebootEscrowRestored(anyByte(), any(), eq(NONSECURE_SECONDARY_USER_ID));
+        assertTrue(metricsSuccessCaptor.getValue());
+    }
+
+    @Test
     public void loadRebootEscrowDataIfAvailable_ServerBased_Success() throws Exception {
         setServerBasedRebootEscrowProvider();
 
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 abd3abe..e64397d 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
@@ -372,8 +372,8 @@
         doReturn(true)
                 .when(mWindowManagerInternal)
                 .setContentRecordingSession(any(ContentRecordingSession.class));
-        ContentRecordingSession taskSession =
-                createTaskSession(mock(IBinder.class), targetUid);
+        ContentRecordingSession taskSession = createTaskSession(mock(IBinder.class));
+        taskSession.setTargetUid(targetUid);
         service.setContentRecordingSession(taskSession);
 
         projection.stop();
@@ -708,8 +708,8 @@
         mService =
                 new MediaProjectionManagerService(mContext, mMediaProjectionMetricsLoggerInjector);
 
-        ContentRecordingSession taskSession =
-                createTaskSession(mock(IBinder.class), targetUid);
+        ContentRecordingSession taskSession = createTaskSession(mock(IBinder.class));
+        taskSession.setTargetUid(targetUid);
         mService.setContentRecordingSession(taskSession);
 
         MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
@@ -915,8 +915,8 @@
                 .setContentRecordingSession(any(ContentRecordingSession.class));
         int targetUid = 123455;
 
-        ContentRecordingSession taskSession =
-                createTaskSession(mock(IBinder.class), targetUid);
+        ContentRecordingSession taskSession = createTaskSession(mock(IBinder.class));
+        taskSession.setTargetUid(targetUid);
         service.setContentRecordingSession(taskSession);
 
         verify(mMediaProjectionMetricsLogger).logInProgress(projection.uid, targetUid);
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 d04c518..f6e1162 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
@@ -16,6 +16,10 @@
 
 package com.android.server.notification;
 
+import static android.service.notification.Condition.SOURCE_USER_ACTION;
+import static android.service.notification.Condition.STATE_FALSE;
+import static android.service.notification.Condition.STATE_TRUE;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static junit.framework.Assert.assertEquals;
@@ -27,16 +31,20 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
+import android.app.Flags;
 import android.content.ComponentName;
 import android.content.ServiceConnection;
 import android.content.pm.IPackageManager;
 import android.net.Uri;
 import android.os.IInterface;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.service.notification.Condition;
 
 import com.android.server.UiServiceTestCase;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -52,6 +60,10 @@
     @Mock
     private ConditionProviders.Callback mCallback;
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(
+            SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -67,8 +79,8 @@
         ManagedServices.ManagedServiceInfo msi = mProviders.new ManagedServiceInfo(
                 mock(IInterface.class), cn, 0, false, mock(ServiceConnection.class), 33, 100);
         Condition[] conditions = new Condition[] {
-                new Condition(Uri.parse("a"), "summary", Condition.STATE_TRUE),
-                new Condition(Uri.parse("b"), "summary2", Condition.STATE_TRUE)
+                new Condition(Uri.parse("a"), "summary", STATE_TRUE),
+                new Condition(Uri.parse("b"), "summary2", STATE_TRUE)
         };
 
         mProviders.notifyConditions("package", msi, conditions);
@@ -85,9 +97,9 @@
                 mock(IInterface.class), new ComponentName("package", "cls"), 0, false,
                 mock(ServiceConnection.class), 33, 100);
         Condition[] conditionsToNotify = new Condition[] {
-                new Condition(Uri.parse("a"), "summary", Condition.STATE_TRUE),
-                new Condition(Uri.parse("b"), "summary2", Condition.STATE_TRUE),
-                new Condition(Uri.parse("c"), "summary3", Condition.STATE_TRUE)
+                new Condition(Uri.parse("a"), "summary", STATE_TRUE),
+                new Condition(Uri.parse("b"), "summary2", STATE_TRUE),
+                new Condition(Uri.parse("c"), "summary3", STATE_TRUE)
         };
 
         mProviders.notifyConditions("package", msi, conditionsToNotify);
@@ -104,10 +116,10 @@
                 mock(IInterface.class), new ComponentName("package", "cls"), 0, false,
                 mock(ServiceConnection.class), 33, 100);
         Condition[] conditionsToNotify = new Condition[] {
-                new Condition(Uri.parse("a"), "summary", Condition.STATE_TRUE),
-                new Condition(Uri.parse("b"), "summary2", Condition.STATE_TRUE),
-                new Condition(Uri.parse("a"), "summary3", Condition.STATE_FALSE),
-                new Condition(Uri.parse("a"), "summary4", Condition.STATE_FALSE)
+                new Condition(Uri.parse("a"), "summary", STATE_TRUE),
+                new Condition(Uri.parse("b"), "summary2", STATE_TRUE),
+                new Condition(Uri.parse("a"), "summary3", STATE_FALSE),
+                new Condition(Uri.parse("a"), "summary4", STATE_FALSE)
         };
 
         mProviders.notifyConditions("package", msi, conditionsToNotify);
@@ -124,10 +136,10 @@
                 mock(IInterface.class), new ComponentName("package", "cls"), 0, false,
                 mock(ServiceConnection.class), 33, 100);
         Condition[] conditionsToNotify = new Condition[] {
-                new Condition(Uri.parse("a"), "summary", Condition.STATE_TRUE),
+                new Condition(Uri.parse("a"), "summary", STATE_TRUE),
                 null,
                 null,
-                new Condition(Uri.parse("b"), "summary", Condition.STATE_TRUE)
+                new Condition(Uri.parse("b"), "summary", STATE_TRUE)
         };
 
         mProviders.notifyConditions("package", msi, conditionsToNotify);
@@ -138,6 +150,57 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_MODES_UI)
+    public void notifyConditions_appCannotUndoUserEnablement() {
+        ManagedServices.ManagedServiceInfo msi = mProviders.new ManagedServiceInfo(
+                mock(IInterface.class), new ComponentName("package", "cls"), 0, false,
+                mock(ServiceConnection.class), 33, 100);
+        // First, user enabled mode
+        Condition[] userConditions = new Condition[] {
+                new Condition(Uri.parse("a"), "summary", STATE_TRUE, SOURCE_USER_ACTION)
+        };
+        mProviders.notifyConditions("package", msi, userConditions);
+        verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(userConditions[0]));
+
+        // Second, app tries to disable it, but cannot
+        Condition[] appConditions = new Condition[] {
+                new Condition(Uri.parse("a"), "summary", STATE_FALSE)
+        };
+        mProviders.notifyConditions("package", msi, appConditions);
+        verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(userConditions[0]));
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_UI)
+    public void notifyConditions_appCanTakeoverUserEnablement() {
+        ManagedServices.ManagedServiceInfo msi = mProviders.new ManagedServiceInfo(
+                mock(IInterface.class), new ComponentName("package", "cls"), 0, false,
+                mock(ServiceConnection.class), 33, 100);
+        // First, user enabled mode
+        Condition[] userConditions = new Condition[] {
+                new Condition(Uri.parse("a"), "summary", STATE_TRUE, SOURCE_USER_ACTION)
+        };
+        mProviders.notifyConditions("package", msi, userConditions);
+        verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(userConditions[0]));
+
+        // Second, app now thinks the rule should be on due it its intelligence
+        Condition[] appConditions = new Condition[] {
+                new Condition(Uri.parse("a"), "summary", STATE_TRUE)
+        };
+        mProviders.notifyConditions("package", msi, appConditions);
+        verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(appConditions[0]));
+
+        // Lastly, app can turn rule off when its intelligence think it should be off
+        appConditions = new Condition[] {
+                new Condition(Uri.parse("a"), "summary", STATE_FALSE)
+        };
+        mProviders.notifyConditions("package", msi, appConditions);
+        verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(appConditions[0]));
+
+        verifyNoMoreInteractions(mCallback);
+    }
+
+    @Test
     public void testRemoveDefaultFromConfig() {
         final int userId = 0;
         ComponentName oldDefaultComponent = ComponentName.unflattenFromString("package/Component1");
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 bfbc81c..4bea95f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/DefaultDeviceEffectsApplierTest.java
@@ -25,10 +25,12 @@
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
@@ -322,4 +324,26 @@
                 argThat(filter -> Intent.ACTION_SCREEN_OFF.equals(filter.getAction(0))),
                 anyInt());
     }
+
+    @Test
+    public void apply_servicesThrow_noCrash() {
+        mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API);
+
+        doThrow(new RuntimeException()).when(mPowerManager)
+                .suppressAmbientDisplay(anyString(), anyBoolean());
+        doThrow(new RuntimeException()).when(mColorDisplayManager).setSaturationLevel(anyInt());
+        doThrow(new RuntimeException()).when(mWallpaperManager).setWallpaperDimAmount(anyFloat());
+        doThrow(new RuntimeException()).when(mUiModeManager).setAttentionModeThemeOverlay(anyInt());
+        mApplier = new DefaultDeviceEffectsApplier(mContext);
+
+        ZenDeviceEffects effects = new ZenDeviceEffects.Builder()
+                .setShouldSuppressAmbientDisplay(true)
+                .setShouldDimWallpaper(true)
+                .setShouldDisplayGrayscale(true)
+                .setShouldUseNightMode(true)
+                .build();
+        mApplier.apply(effects, UPDATE_ORIGIN_USER);
+
+        // No crashes
+    }
 }
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 c7c97e4..8a7d276 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -57,12 +57,12 @@
 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.platform.test.flag.junit.SetFlagsRule;
 import android.service.notification.StatusBarNotification;
 import android.util.ArrayMap;
 
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.R;
 import com.android.server.UiServiceTestCase;
@@ -79,9 +79,12 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
 @SmallTest
 @SuppressLint("GuardedBy") // It's ok for this test to access guarded methods from the class.
-@RunWith(AndroidJUnit4.class)
+@RunWith(ParameterizedAndroidJunit4.class)
 public class GroupHelperTest extends UiServiceTestCase {
     @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
@@ -95,6 +98,16 @@
     private GroupHelper mGroupHelper;
     private @Mock Icon mSmallIcon;
 
+    @Parameters(name = "{0}")
+    public static List<FlagsParameterization> getParams() {
+        return FlagsParameterization.allCombinationsOf(
+                android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST);
+    }
+
+    public GroupHelperTest(FlagsParameterization flags) {
+        mSetFlagsRule.setFlagsParameterization(flags);
+    }
+
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
@@ -708,7 +721,8 @@
     }
 
     @Test
-    public void testDropToZeroRemoveGroup() {
+    @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
+    public void testDropToZeroRemoveGroup_disableFlag() {
         final String pkg = "package";
         List<StatusBarNotification> posted = new ArrayList<>();
         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
@@ -736,7 +750,37 @@
     }
 
     @Test
-    public void testAppStartsGrouping() {
+    @EnableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
+    public void testDropToZeroRemoveGroup() {
+        final String pkg = "package";
+        List<StatusBarNotification> posted = new ArrayList<>();
+        for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+            final StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
+            posted.add(sbn);
+            mGroupHelper.onNotificationPosted(sbn, false);
+        }
+        verify(mCallback, times(1)).addAutoGroupSummary(
+                anyInt(), eq(pkg), anyString(), eq(getNotificationAttributes(BASE_FLAGS)));
+        verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).addAutoGroup(anyString(), anyBoolean());
+        verify(mCallback, never()).removeAutoGroup(anyString());
+        verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+        Mockito.reset(mCallback);
+
+        for (int i = 0; i < AUTOGROUP_AT_COUNT - 1; i++) {
+            mGroupHelper.onNotificationRemoved(posted.remove(0));
+        }
+        verify(mCallback, never()).removeAutoGroup(anyString());
+        verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+        Mockito.reset(mCallback);
+
+        mGroupHelper.onNotificationRemoved(posted.remove(0));
+        verify(mCallback, never()).removeAutoGroup(anyString());
+        verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), anyString());
+    }
+
+    @Test
+    @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
+    public void testAppStartsGrouping_disableFlag() {
         final String pkg = "package";
         List<StatusBarNotification> posted = new ArrayList<>();
         for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
@@ -765,6 +809,36 @@
     }
 
     @Test
+    @EnableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
+    public void testAppStartsGrouping() {
+        final String pkg = "package";
+        List<StatusBarNotification> posted = new ArrayList<>();
+        for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+            final StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
+            posted.add(sbn);
+            mGroupHelper.onNotificationPosted(sbn, false);
+        }
+        verify(mCallback, times(1)).addAutoGroupSummary(
+                anyInt(), eq(pkg), anyString(), eq(getNotificationAttributes(BASE_FLAGS)));
+        verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).addAutoGroup(anyString(), anyBoolean());
+        verify(mCallback, never()).removeAutoGroup(anyString());
+        verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+        Mockito.reset(mCallback);
+
+        for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+            final StatusBarNotification sbn =
+                    getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, "app group");
+            sbn.setOverrideGroupKey("autogrouped");
+            mGroupHelper.onNotificationPosted(sbn, true);
+            verify(mCallback, times(1)).removeAutoGroup(sbn.getKey());
+            if (i < AUTOGROUP_AT_COUNT - 1) {
+                verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+            }
+        }
+        verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), anyString());
+    }
+
+    @Test
     @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
     public void testNewNotificationsAddedToAutogroup_ifOriginalNotificationsCanceled_alwaysGroup() {
         final String pkg = "package";
@@ -915,8 +989,9 @@
     }
 
     @Test
+    @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
     @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
-    public void testAddSummary_diffIcon_diffColor() {
+    public void testAddSummary_diffIcon_diffColor_disableFlag() {
         final String pkg = "package";
         final Icon initialIcon = mock(Icon.class);
         when(initialIcon.sameAs(initialIcon)).thenReturn(true);
@@ -959,6 +1034,51 @@
     }
 
     @Test
+    @EnableFlags({Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE,
+            android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST})
+    public void testAddSummary_diffIcon_diffColor() {
+        final String pkg = "package";
+        final Icon initialIcon = mock(Icon.class);
+        when(initialIcon.sameAs(initialIcon)).thenReturn(true);
+        final int initialIconColor = Color.BLUE;
+
+        // Spy GroupHelper for getMonochromeAppIcon
+        final Icon monochromeIcon = mock(Icon.class);
+        when(monochromeIcon.sameAs(monochromeIcon)).thenReturn(true);
+        GroupHelper groupHelper = spy(mGroupHelper);
+        doReturn(monochromeIcon).when(groupHelper).getMonochromeAppIcon(eq(pkg));
+
+        final NotificationAttributes initialAttr = new NotificationAttributes(BASE_FLAGS,
+                initialIcon, initialIconColor, DEFAULT_VISIBILITY);
+
+        // Add notifications with same icon and color
+        for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+            StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, null,
+                    initialIcon, initialIconColor);
+            groupHelper.onNotificationPosted(sbn, false);
+        }
+        // Check that the summary would have the same icon and color
+        verify(mCallback, times(1)).addAutoGroupSummary(
+                anyInt(), eq(pkg), anyString(), eq(initialAttr));
+        verify(mCallback, times(AUTOGROUP_AT_COUNT - 1)).addAutoGroup(anyString(), anyBoolean());
+        verify(mCallback, never()).removeAutoGroup(anyString());
+        verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+
+        // After auto-grouping, add new notification with a different color
+        final Icon newIcon = mock(Icon.class);
+        final int newIconColor = Color.YELLOW;
+        StatusBarNotification sbn = getSbn(pkg, AUTOGROUP_AT_COUNT,
+                String.valueOf(AUTOGROUP_AT_COUNT), UserHandle.SYSTEM, null, newIcon,
+                newIconColor);
+        groupHelper.onNotificationPosted(sbn, true);
+
+        // Summary should be updated to the default color and the icon to the monochrome icon
+        NotificationAttributes newAttr = new NotificationAttributes(BASE_FLAGS, monochromeIcon,
+                COLOR_DEFAULT, DEFAULT_VISIBILITY);
+        verify(mCallback, times(1)).updateAutogroupSummary(anyInt(), anyString(), eq(newAttr));
+    }
+
+    @Test
     @DisableFlags(android.app.Flags.FLAG_CHECK_AUTOGROUP_BEFORE_POST)
     @EnableFlags(Flags.FLAG_AUTOGROUP_SUMMARY_ICON_UPDATE)
     public void testAddSummary_diffVisibility_alwaysAutogroup() {
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 e4188b0..72ace84 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -6168,6 +6168,23 @@
                 .isEqualTo(previousManualZenPolicy);
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_MODES_API)
+    public void addRule_iconIdWithResourceNameTooLong_ignoresIcon() {
+        int resourceId = 999;
+        String veryLongResourceName = "com.android.server.notification:drawable/"
+                + "omg_this_is_one_long_resource_name".repeat(100);
+        when(mResources.getResourceName(resourceId)).thenReturn(veryLongResourceName);
+        when(mResources.getIdentifier(veryLongResourceName, null, null)).thenReturn(resourceId);
+
+        String ruleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+                new AutomaticZenRule.Builder("Rule", CONDITION_ID).setIconResId(resourceId).build(),
+                UPDATE_ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+        AutomaticZenRule storedRule = mZenModeHelper.getAutomaticZenRule(ruleId);
+
+        assertThat(storedRule.getIconResId()).isEqualTo(0);
+    }
+
     private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode,
             @Nullable ZenPolicy zenPolicy) {
         ZenRule rule = new ZenRule();
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 7031975..e91fd37 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2057,7 +2057,7 @@
         final ActivityRecord activity = createActivityWithTask();
         // TaskSnapshotSurface requires a fullscreen opaque window.
         final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
-                WindowManager.LayoutParams.TYPE_APPLICATION_STARTING);
+                TYPE_BASE_APPLICATION);
         params.width = params.height = WindowManager.LayoutParams.MATCH_PARENT;
         final TestWindowState w = new TestWindowState(
                 mAtm.mWindowManager, getTestSession(), new TestIWindow(), params, activity);
@@ -2504,25 +2504,6 @@
         activity.removeImmediately();
     }
 
-    @Test
-    @Presubmit
-    public void testGetTopFullscreenOpaqueWindow() {
-        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
-        assertNull(activity.getTopFullscreenOpaqueWindow());
-
-        final WindowState window1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "window1");
-        final WindowState window11 = createWindow(null, TYPE_APPLICATION, activity, "window11");
-        final WindowState window12 = createWindow(null, TYPE_APPLICATION, activity, "window12");
-        assertEquals(window12, activity.getTopFullscreenOpaqueWindow());
-        window12.mAttrs.width = 500;
-        assertEquals(window11, activity.getTopFullscreenOpaqueWindow());
-        window11.mAttrs.width = 500;
-        assertEquals(window1, activity.getTopFullscreenOpaqueWindow());
-        window1.mAttrs.alpha = 0f;
-        assertNull(activity.getTopFullscreenOpaqueWindow());
-        activity.removeImmediately();
-    }
-
     @SetupWindows(addWindows = W_ACTIVITY)
     @Test
     public void testLandscapeSeascapeRotationByApp() {
@@ -2745,6 +2726,19 @@
         assertNoStartingWindow(activity);
     }
 
+    @Test
+    public void testPostCleanupStartingWindow() {
+        registerTestStartingWindowOrganizer();
+        final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
+        activity.addStartingWindow(mPackageName, android.R.style.Theme, null, true, true, false,
+                true, false, false, false);
+        waitUntilHandlersIdle();
+        assertHasStartingWindow(activity);
+        // Simulate Shell remove starting window actively.
+        activity.mStartingWindow.removeImmediately();
+        assertNoStartingWindow(activity);
+    }
+
     private void testLegacySplashScreen(int targetSdk, int verifyType) {
         final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true).build();
         activity.mTargetSdk = targetSdk;
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 96ddfe8..7ced9d5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -107,6 +107,7 @@
 import android.os.Binder;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.annotations.RequiresFlagsDisabled;
 import android.provider.DeviceConfig;
@@ -401,6 +402,7 @@
     // TODO(b/333663877): Enable test after fix
     @Test
     @RequiresFlagsDisabled({Flags.FLAG_INSETS_DECOUPLED_CONFIGURATION})
+    @EnableFlags(Flags.FLAG_IMMERSIVE_APP_REPOSITIONING)
     public void testRepositionLandscapeImmersiveAppWithDisplayCutout() {
         final int dw = 2100;
         final int dh = 2000;
@@ -4059,6 +4061,7 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_IMMERSIVE_APP_REPOSITIONING)
     public void testImmersiveLetterboxAlignedToBottom_OverlappingNavbar() {
         assertLandscapeActivityAlignedToBottomWithNavbar(true /* immersive */);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 80c066d..69f2d68 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1446,6 +1446,33 @@
     }
 
     @Test
+    public void testTransitionEndedListeners() {
+        final TransitionController controller = new TestTransitionController(mAtm);
+        controller.setSyncEngine(mWm.mSyncEngine);
+        final ITransitionPlayer player = new ITransitionPlayer.Default();
+        controller.registerTransitionPlayer(player, null /* playerProc */);
+        final Runnable transitionEndedListener = mock(Runnable.class);
+
+        final Transition transition1 = controller.createTransition(TRANSIT_OPEN);
+        transition1.addTransitionEndedListener(transitionEndedListener);
+
+        // Using abort to force-finish the sync (since we can't wait for drawing in unit test).
+        // We didn't call abort on the transition itself, so it will still run onTransactionReady
+        // normally.
+        mWm.mSyncEngine.abort(transition1.getSyncId());
+        transition1.finishTransition();
+
+        verify(transitionEndedListener).run();
+
+        clearInvocations(transitionEndedListener);
+
+        final Transition transition2 = controller.createTransition(TRANSIT_OPEN);
+        transition2.addTransitionEndedListener(transitionEndedListener);
+        transition2.abort();
+        verify(transitionEndedListener).run();
+    }
+
+    @Test
     public void testTransientLaunch() {
         spyOn(mWm.mSnapshotController.mTaskSnapshotController);
         final ArrayList<ActivityRecord> enteringAnimReports = new ArrayList<>();
@@ -1549,8 +1576,10 @@
         });
         assertTrue(activity1.isVisible());
         doReturn(false).when(task1).isTranslucent(null);
+        doReturn(false).when(task1).isTranslucentForTransition();
         assertTrue(controller.canApplyDim(task1));
         doReturn(true).when(task1).isTranslucent(null);
+        doReturn(true).when(task1).isTranslucentForTransition();
         assertFalse(controller.canApplyDim(task1));
 
         controller.finishTransition(closeTransition);
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 7e6301f..24ebad6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -795,39 +795,55 @@
     }
 
     @Test
-    public void testGetTaskWindowContainerTokenForLaunchCookie_nullCookie() {
-        WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(null);
-        assertThat(wci).isNull();
-    }
-
-    @Test
-    public void testGetTaskWindowContainerTokenForLaunchCookie_invalidCookie() {
+    public void testGetTaskWindowContainerTokenForRecordingSession_invalidCookie() {
         Binder cookie = new Binder("test cookie");
-        WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(cookie);
+        WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForRecordingSession(
+                ContentRecordingSession.createTaskSession(cookie));
         assertThat(wci).isNull();
 
         final ActivityRecord testActivity = new ActivityBuilder(mAtm)
                 .setCreateTask(true)
                 .build();
 
-        wci = mWm.getTaskWindowContainerInfoForLaunchCookie(cookie);
+        wci = mWm.getTaskWindowContainerInfoForRecordingSession(
+                ContentRecordingSession.createTaskSession(cookie));
         assertThat(wci).isNull();
     }
 
     @Test
-    public void testGetTaskWindowContainerTokenForLaunchCookie_validCookie() {
+    public void testGetTaskWindowContainerTokenForRecordingSession_validCookie() {
         final Binder cookie = new Binder("ginger cookie");
         final WindowContainerToken launchRootTask = mock(WindowContainerToken.class);
         final int uid = 123;
         setupActivityWithLaunchCookie(cookie, launchRootTask, uid);
 
-        WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(cookie);
+        WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForRecordingSession(
+                ContentRecordingSession.createTaskSession(cookie));
         mExpect.that(wci.getToken()).isEqualTo(launchRootTask);
         mExpect.that(wci.getUid()).isEqualTo(uid);
     }
 
     @Test
-    public void testGetTaskWindowContainerTokenForLaunchCookie_multipleCookies() {
+    public void testGetTaskWindowContainerTokenForRecordingSession_validTaskId() {
+        final WindowContainerToken launchRootTask = mock(WindowContainerToken.class);
+        final WindowContainer.RemoteToken remoteToken = mock(WindowContainer.RemoteToken.class);
+        when(remoteToken.toWindowContainerToken()).thenReturn(launchRootTask);
+
+        final int uid = 123;
+        final ActivityRecord testActivity =
+                new ActivityBuilder(mAtm).setCreateTask(true).setUid(uid).build();
+        testActivity.mLaunchCookie = null;
+        testActivity.getTask().mRemoteToken = remoteToken;
+
+        WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForRecordingSession(
+                ContentRecordingSession.createTaskSession(
+                        new Binder("cookie"), testActivity.getTask().mTaskId));
+        mExpect.that(wci.getToken()).isEqualTo(launchRootTask);
+        mExpect.that(wci.getUid()).isEqualTo(uid);
+    }
+
+    @Test
+    public void testGetTaskWindowContainerTokenForRecordingSession_multipleCookies() {
         final Binder cookie1 = new Binder("ginger cookie");
         final WindowContainerToken launchRootTask1 = mock(WindowContainerToken.class);
         final int uid1 = 123;
@@ -839,13 +855,14 @@
         setupActivityWithLaunchCookie(new Binder("peanut butter cookie"),
                 mock(WindowContainerToken.class), /* uid= */ 789);
 
-        WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(cookie1);
+        WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForRecordingSession(
+                ContentRecordingSession.createTaskSession(cookie1));
         mExpect.that(wci.getToken()).isEqualTo(launchRootTask1);
         mExpect.that(wci.getUid()).isEqualTo(uid1);
     }
 
     @Test
-    public void testGetTaskWindowContainerTokenForLaunchCookie_multipleCookies_noneValid() {
+    public void testGetTaskWindowContainerTokenForRecordingSession_multipleCookies_noneValid() {
         setupActivityWithLaunchCookie(new Binder("ginger cookie"),
                 mock(WindowContainerToken.class), /* uid= */ 123);
 
@@ -855,8 +872,8 @@
         setupActivityWithLaunchCookie(new Binder("peanut butter cookie"),
                 mock(WindowContainerToken.class), /* uid= */ 789);
 
-        WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForLaunchCookie(
-                new Binder("some other cookie"));
+        WindowContainerInfo wci = mWm.getTaskWindowContainerInfoForRecordingSession(
+                ContentRecordingSession.createTaskSession(new Binder("some other cookie")));
         assertThat(wci).isNull();
     }
 
@@ -895,6 +912,7 @@
     public void setContentRecordingSession_sessionContentTask_matchingTask_returnsTrue() {
         WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
         ActivityRecord activityRecord = createActivityRecord(createTask(mDefaultDisplay));
+        activityRecord.mLaunchCookie = new Binder();
         ContentRecordingSession session = ContentRecordingSession.createTaskSession(
                 activityRecord.mLaunchCookie);
 
@@ -908,6 +926,7 @@
         WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
         Task task = createTask(mDefaultDisplay);
         ActivityRecord activityRecord = createActivityRecord(task);
+        activityRecord.mLaunchCookie = new Binder();
         ContentRecordingSession session =
                 ContentRecordingSession.createTaskSession(activityRecord.mLaunchCookie);
 
@@ -915,7 +934,7 @@
 
         mExpect.that(session.getTokenToRecord())
                 .isEqualTo(task.mRemoteToken.toWindowContainerToken().asBinder());
-        mExpect.that(session.getTargetUid()).isEqualTo(activityRecord.getUid());
+        mExpect.that(session.getTargetUid()).isEqualTo(task.effectiveUid);
     }
 
     @Test
diff --git a/telephony/java/android/telephony/CarrierRestrictionRules.java b/telephony/java/android/telephony/CarrierRestrictionRules.java
index d5db612..65e8e13 100644
--- a/telephony/java/android/telephony/CarrierRestrictionRules.java
+++ b/telephony/java/android/telephony/CarrierRestrictionRules.java
@@ -555,10 +555,11 @@
          * Set the device's carrier restriction status
          *
          * @param carrierRestrictionStatus device restriction status
-         * @hide
          */
         public @NonNull
-        Builder setCarrierRestrictionStatus(int carrierRestrictionStatus) {
+        @FlaggedApi(Flags.FLAG_SET_CARRIER_RESTRICTION_STATUS)
+        Builder setCarrierRestrictionStatus(
+                @CarrierRestrictionStatus int carrierRestrictionStatus) {
             mRules.mCarrierRestrictionStatus = carrierRestrictionStatus;
             return this;
         }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt b/telephony/java/android/telephony/satellite/SystemSelectionSpecifier.aidl
similarity index 62%
copy from packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt
copy to telephony/java/android/telephony/satellite/SystemSelectionSpecifier.aidl
index 277dbb7..ecd248c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconLabelVisibilityRepositoryKosmos.kt
+++ b/telephony/java/android/telephony/satellite/SystemSelectionSpecifier.aidl
@@ -1,11 +1,11 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright 2024, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *      http://www.apache.org/licenses/LICENSE-2.0
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.systemui.qs.panels.data.repository
+ package android.telephony.satellite;
 
-import com.android.systemui.kosmos.Kosmos
-
-val Kosmos.iconLabelVisibilityRepository by Kosmos.Fixture { IconLabelVisibilityRepository() }
+ parcelable SystemSelectionSpecifier;
diff --git a/telephony/java/android/telephony/satellite/SystemSelectionSpecifier.java b/telephony/java/android/telephony/satellite/SystemSelectionSpecifier.java
new file mode 100644
index 0000000..8a5e7f2
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SystemSelectionSpecifier.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.IntArray;
+
+import java.util.Objects;
+
+/**
+ * @hide
+ */
+public final class SystemSelectionSpecifier implements Parcelable {
+
+    /** Network plmn associated with channel information. */
+    @NonNull private String mMccMnc;
+
+    /** The frequency bands to scan. Maximum length of the vector is 8. */
+    @NonNull private IntArray mBands;
+
+    /**
+     * The radio channels to scan as defined in 3GPP TS 25.101 and 36.101.
+     * Maximum length of the vector is 32.
+     */
+    @NonNull private IntArray mEarfcs;
+
+    /**
+     * @hide
+     */
+    public SystemSelectionSpecifier(@NonNull String mccmnc, @NonNull IntArray bands,
+            @NonNull IntArray earfcs) {
+        mMccMnc = mccmnc;
+        mBands = bands;
+        mEarfcs = earfcs;
+    }
+
+    private SystemSelectionSpecifier(Parcel in) {
+        readFromParcel(in);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        mMccMnc = TextUtils.emptyIfNull(mMccMnc);
+        out.writeString8(mMccMnc);
+
+        if (mBands != null && mBands.size() > 0) {
+            out.writeInt(mBands.size());
+            for (int i = 0; i < mBands.size(); i++) {
+                out.writeInt(mBands.get(i));
+            }
+        } else {
+            out.writeInt(0);
+        }
+
+        if (mEarfcs != null && mEarfcs.size() > 0) {
+            out.writeInt(mEarfcs.size());
+            for (int i = 0; i < mEarfcs.size(); i++) {
+                out.writeInt(mEarfcs.get(i));
+            }
+        } else {
+            out.writeInt(0);
+        }
+    }
+
+    @NonNull public static final Parcelable.Creator<SystemSelectionSpecifier> CREATOR =
+            new Creator<>() {
+                @Override
+                public SystemSelectionSpecifier createFromParcel(Parcel in) {
+                    return new SystemSelectionSpecifier(in);
+                }
+
+                @Override
+                public SystemSelectionSpecifier[] newArray(int size) {
+                    return new SystemSelectionSpecifier[size];
+                }
+            };
+
+    @Override
+    @NonNull public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("mccmnc:");
+        sb.append(mMccMnc);
+        sb.append(",");
+
+        sb.append("bands:");
+        if (mBands != null && mBands.size() > 0) {
+            for (int i = 0; i < mBands.size(); i++) {
+                sb.append(mBands.get(i));
+                sb.append(",");
+            }
+        } else {
+            sb.append("none,");
+        }
+
+        sb.append("earfcs:");
+        if (mEarfcs != null && mEarfcs.size() > 0) {
+            for (int i = 0; i < mEarfcs.size(); i++) {
+                sb.append(mEarfcs.get(i));
+                sb.append(",");
+            }
+        } else {
+            sb.append("none");
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        SystemSelectionSpecifier that = (SystemSelectionSpecifier) o;
+        return Objects.equals(mMccMnc, that.mMccMnc)
+                && Objects.equals(mBands, that.mBands)
+                && Objects.equals(mEarfcs, that.mEarfcs);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mMccMnc, mBands, mEarfcs);
+    }
+
+    @NonNull public String getMccMnc() {
+        return mMccMnc;
+    }
+
+    @NonNull public IntArray getBands() {
+        return mBands;
+    }
+
+    @NonNull public IntArray getEarfcs() {
+        return mEarfcs;
+    }
+
+    private void readFromParcel(Parcel in) {
+        mMccMnc = in.readString();
+
+        mBands = new IntArray();
+        int numBands = in.readInt();
+        if (numBands > 0) {
+            for (int i = 0; i < numBands; i++) {
+                mBands.add(in.readInt());
+            }
+        }
+
+        mEarfcs = new IntArray();
+        int numEarfcs = in.readInt();
+        if (numEarfcs > 0) {
+            for (int i = 0; i < numEarfcs; i++) {
+                mEarfcs.add(in.readInt());
+            }
+        }
+    }
+}
diff --git a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
index 16983a0..b82396e 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
@@ -23,6 +23,7 @@
 import android.telephony.satellite.stub.ISatelliteCapabilitiesConsumer;
 import android.telephony.satellite.stub.ISatelliteListener;
 import android.telephony.satellite.stub.SatelliteDatagram;
+import android.telephony.satellite.stub.SystemSelectionSpecifier;
 
 /**
  * {@hide}
@@ -500,4 +501,21 @@
       *   SatelliteResult:SATELLITE_RESULT_REQUEST_NOT_SUPPORTED
       */
      void abortSendingSatelliteDatagrams(in IIntegerConsumer resultCallback);
+
+     /**
+      * Request to update the satellite subscription to be used for Non-Terrestrial network.
+      *
+      * @param iccId The ICCID of the subscription
+      * @param resultCallback The callback to receive the error code result of the operation.
+      */
+     void updateSatelliteSubscription(in String iccId, in IIntegerConsumer resultCallback);
+
+     /**
+      * Request to update system selection channels
+      *
+      * @param systemSelectionSpecifiers list of system selection specifiers
+      * @param resultCallback The callback to receive the error code result of the operation.
+      */
+     void updateSystemSelectionChannels(in List<SystemSelectionSpecifier> systemSelectionSpecifiers,
+            in IIntegerConsumer resultCallback);
 }
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
index a623633..d8b4974 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
@@ -262,6 +262,22 @@
                     "abortSendingSatelliteDatagrams");
         }
 
+        @Override
+        public void updateSatelliteSubscription(String iccId, IIntegerConsumer resultCallback)
+                throws RemoteException {
+            executeMethodAsync(() -> SatelliteImplBase.this.updateSatelliteSubscription(
+                    iccId, resultCallback), "updateSatelliteSubscription");
+        }
+
+        @Override
+        public void updateSystemSelectionChannels(
+                List<SystemSelectionSpecifier> systemSelectionSpecifiers,
+                IIntegerConsumer resultCallback) throws RemoteException {
+            executeMethodAsync(() -> SatelliteImplBase.this.updateSystemSelectionChannels(
+                    systemSelectionSpecifiers, resultCallback),
+                    "updateSystemSelectionChannels");
+        }
+
         // Call the methods with a clean calling identity on the executor and wait indefinitely for
         // the future to return.
         private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
@@ -768,4 +784,27 @@
     public void abortSendingSatelliteDatagrams(@NonNull IIntegerConsumer resultCallback){
         // stub implementation
     }
+
+    /**
+     * Request to update the satellite subscription to be used for Non-Terrestrial network.
+     *
+     * @param iccId The ICCID of the subscription
+     * @param resultCallback The callback to receive the error code result of the operation.
+     */
+    public void updateSatelliteSubscription(String iccId,
+            @NonNull IIntegerConsumer resultCallback) {
+        // stub implementation
+    }
+
+    /**
+     * Request to update system selection channels
+     *
+     * @param systemSelectionSpecifiers list of system selection specifiers
+     * @param resultCallback The callback to receive the error code result of the operation.
+     */
+    public void updateSystemSelectionChannels(
+            @NonNull List<SystemSelectionSpecifier> systemSelectionSpecifiers,
+            @NonNull IIntegerConsumer resultCallback) {
+        // stub implementation
+    }
 }
diff --git a/telephony/java/android/telephony/satellite/stub/SystemSelectionSpecifier.aidl b/telephony/java/android/telephony/satellite/stub/SystemSelectionSpecifier.aidl
new file mode 100644
index 0000000..22240f6
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/stub/SystemSelectionSpecifier.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.satellite.stub;
+
+/**
+ * {@hide}
+ */
+parcelable SystemSelectionSpecifier {
+    /** Network plmn associated with channel information. */
+    String mMccMnc;
+
+    /**
+     * The frequency bands to scan. Bands and earfcns won't overlap.
+     * Bands will be filled only if the whole band is needed.
+     * Maximum length of the vector is 8.
+     */
+    int[] mBands;
+
+    /**
+     * The radio channels to scan as defined in 3GPP TS 25.101 and 36.101.
+     * Maximum length of the vector is 32.
+     */
+    int[] mEarfcs;
+}
diff --git a/tests/FlickerTests/ActivityEmbedding/Android.bp b/tests/FlickerTests/ActivityEmbedding/Android.bp
index 2cdf542..e09fbf6 100644
--- a/tests/FlickerTests/ActivityEmbedding/Android.bp
+++ b/tests/FlickerTests/ActivityEmbedding/Android.bp
@@ -20,17 +20,65 @@
     // all of the 'license_kinds' from "frameworks_base_license"
     // to get the below license kinds:
     //   SPDX-license-identifier-Apache-2.0
+    default_team: "trendy_team_windowing_sdk",
     default_applicable_licenses: ["frameworks_base_license"],
 }
 
-android_test {
-    name: "FlickerTestsOther",
+filegroup {
+    name: "FlickerTestsOtherCommon-src",
+    srcs: ["src/**/ActivityEmbeddingTestBase.kt"],
+}
+
+filegroup {
+    name: "FlickerTestsOtherOpen-src",
+    srcs: ["src/**/open/*"],
+}
+
+filegroup {
+    name: "FlickerTestsOtherRotation-src",
+    srcs: ["src/**/rotation/*"],
+}
+
+java_library {
+    name: "FlickerTestsOtherCommon",
+    defaults: ["FlickerTestsDefault"],
+    srcs: [":FlickerTestsOtherCommon-src"],
+    static_libs: ["FlickerTestsBase"],
+}
+
+java_defaults {
+    name: "FlickerTestsOtherDefaults",
     defaults: ["FlickerTestsDefault"],
     manifest: "AndroidManifest.xml",
     package_name: "com.android.server.wm.flicker",
     instrumentation_target_package: "com.android.server.wm.flicker",
     test_config_template: "AndroidTestTemplate.xml",
-    srcs: ["src/**/*"],
-    static_libs: ["FlickerTestsBase"],
+    static_libs: [
+        "FlickerTestsBase",
+        "FlickerTestsOtherCommon",
+    ],
     data: ["trace_config/*"],
 }
+
+android_test {
+    name: "FlickerTestsOtherOpen",
+    defaults: ["FlickerTestsOtherDefaults"],
+    srcs: [":FlickerTestsOtherOpen-src"],
+}
+
+android_test {
+    name: "FlickerTestsOtherRotation",
+    defaults: ["FlickerTestsOtherDefaults"],
+    srcs: [":FlickerTestsOtherRotation-src"],
+}
+
+android_test {
+    name: "FlickerTestsOther",
+    defaults: ["FlickerTestsOtherDefaults"],
+    srcs: ["src/**/*"],
+    exclude_srcs: [
+        ":FlickerTestsOtherOpen-src",
+        ":FlickerTestsOtherRotation-src",
+        ":FlickerTestsOtherCommon-src",
+    ],
+}
diff --git a/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/Utils.kt b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/Utils.kt
index 8a241de..209a14b 100644
--- a/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/Utils.kt
+++ b/tests/FlickerTests/FlickerService/src/com/android/server/wm/flicker/service/Utils.kt
@@ -17,6 +17,7 @@
 package com.android.server.wm.flicker.service
 
 import android.app.Instrumentation
+import android.platform.test.rule.DisableNotificationCooldownSettingRule
 import android.platform.test.rule.NavigationModeRule
 import android.platform.test.rule.PressHomeRule
 import android.platform.test.rule.UnlockScreenRule
@@ -48,6 +49,7 @@
                     clearCacheAfterParsing = false
                 )
             )
+            .around(DisableNotificationCooldownSettingRule())
             .around(PressHomeRule())
     }
 }
diff --git a/tests/FlickerTests/IME/Android.bp b/tests/FlickerTests/IME/Android.bp
index 3538949c..ccc3683 100644
--- a/tests/FlickerTests/IME/Android.bp
+++ b/tests/FlickerTests/IME/Android.bp
@@ -39,6 +39,10 @@
     defaults: ["FlickerTestsDefault"],
     manifest: "AndroidManifest.xml",
     test_config_template: "AndroidTestTemplate.xml",
+    test_suites: [
+        "device-tests",
+        "device-platinum-tests",
+    ],
     srcs: ["src/**/*"],
     static_libs: ["FlickerTestsBase"],
     data: ["trace_config/*"],
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
index dc50135..ed6e8df 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
@@ -23,6 +23,7 @@
 import android.tools.flicker.legacy.LegacyFlickerTest
 import android.tools.flicker.legacy.LegacyFlickerTestFactory
 import android.tools.traces.component.ComponentNameMatcher
+import androidx.test.filters.FlakyTest
 import com.android.server.wm.flicker.BaseTest
 import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
 import org.junit.FixMethodOrder
@@ -77,6 +78,7 @@
 
     @Presubmit @Test fun imeLayerBecomesInvisible() = flicker.imeLayerBecomesInvisible()
 
+    @FlakyTest(bugId = 330486656)
     @Presubmit
     @Test
     fun imeAppLayerIsAlwaysVisible() {
diff --git a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
index c29e71c..07fc230 100644
--- a/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
+++ b/tests/FlickerTests/Notification/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt
@@ -18,6 +18,7 @@
 
 import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
+import android.platform.test.rule.DisableNotificationCooldownSettingRule
 import android.tools.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.flicker.legacy.FlickerBuilder
 import android.tools.flicker.legacy.FlickerTestData
@@ -37,6 +38,7 @@
 import com.android.server.wm.flicker.taskBarLayerIsVisibleAtEnd
 import com.android.server.wm.flicker.taskBarWindowIsVisibleAtEnd
 import org.junit.Assume
+import org.junit.ClassRule
 import org.junit.FixMethodOrder
 import org.junit.Ignore
 import org.junit.Test
@@ -208,5 +210,10 @@
         @Parameterized.Parameters(name = "{0}")
         @JvmStatic
         fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
+
+        /** Ensures that posted notifications will alert and HUN even just after boot. */
+        @ClassRule
+        @JvmField
+        val disablenotificationCooldown = DisableNotificationCooldownSettingRule()
     }
 }
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index a85d809..c0cbdc3 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -31,6 +31,7 @@
         "androidx.test.runner",
         "androidx.test.uiautomator_uiautomator",
         "compatibility-device-util-axt",
+        "cts-input-lib",
         "flag-junit",
         "frameworks-base-testutils",
         "hamcrest-library",
diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt
index 4893d14..6b95f5c 100644
--- a/tests/Input/src/com/android/test/input/AnrTest.kt
+++ b/tests/Input/src/com/android/test/input/AnrTest.kt
@@ -21,21 +21,23 @@
 
 import android.app.ActivityManager
 import android.app.ApplicationExitInfo
+import android.content.Context
 import android.graphics.Rect
+import android.hardware.display.DisplayManager
 import android.os.Build
 import android.os.IInputConstants.UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS
 import android.os.SystemClock
 import android.provider.Settings
 import android.provider.Settings.Global.HIDE_ERROR_DIALOGS
 import android.testing.PollingCheck
-import android.view.InputDevice
-import android.view.MotionEvent
 
 import androidx.test.uiautomator.By
 import androidx.test.uiautomator.UiDevice
 import androidx.test.uiautomator.UiObject2
 import androidx.test.uiautomator.Until
 
+import com.android.cts.input.UinputTouchScreen
+
 import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
@@ -150,6 +152,18 @@
         assertEquals(ApplicationExitInfo.REASON_ANR, reasons[0].reason)
     }
 
+    private fun clickOnObject(obj: UiObject2) {
+        val displayManager =
+            instrumentation.context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
+        val display = displayManager.getDisplay(obj.getDisplayId())
+        val touchScreen = UinputTouchScreen(instrumentation, display)
+
+        val rect: Rect = obj.visibleBounds
+        val pointer = touchScreen.touchDown(rect.centerX(), rect.centerY())
+        pointer.lift()
+        touchScreen.close()
+    }
+
     private fun triggerAnr() {
         startUnresponsiveActivity()
         val uiDevice: UiDevice = UiDevice.getInstance(instrumentation)
@@ -160,13 +174,7 @@
             return
         }
 
-        val rect: Rect = obj.visibleBounds
-        val downTime = SystemClock.uptimeMillis()
-        val downEvent = MotionEvent.obtain(downTime, downTime,
-                MotionEvent.ACTION_DOWN, rect.left.toFloat(), rect.top.toFloat(), 0 /* metaState */)
-        downEvent.source = InputDevice.SOURCE_TOUCHSCREEN
-
-        instrumentation.uiAutomation.injectInputEvent(downEvent, false /* sync*/)
+        clickOnObject(obj)
 
         SystemClock.sleep(DISPATCHING_TIMEOUT.toLong()) // default ANR timeout for gesture monitors
     }
diff --git a/tests/InputScreenshotTest/robotests/Android.bp b/tests/InputScreenshotTest/robotests/Android.bp
index 384f58a..d63fd69 100644
--- a/tests/InputScreenshotTest/robotests/Android.bp
+++ b/tests/InputScreenshotTest/robotests/Android.bp
@@ -69,4 +69,6 @@
     upstream: true,
     java_resource_dirs: ["config"],
     instrumentation_for: "InputRoboApp",
+
+    strict_mode: false,
 }
diff --git a/tests/TrustTests/AndroidManifest.xml b/tests/TrustTests/AndroidManifest.xml
index 30cf345..2f6c0dd 100644
--- a/tests/TrustTests/AndroidManifest.xml
+++ b/tests/TrustTests/AndroidManifest.xml
@@ -78,6 +78,7 @@
                 <action android:name="android.service.trust.TrustAgentService" />
             </intent-filter>
         </service>
+
         <service
             android:name=".IsActiveUnlockRunningTrustAgent"
             android:exported="true"
@@ -88,6 +89,16 @@
             </intent-filter>
         </service>
 
+        <service
+            android:name=".UnlockAttemptTrustAgent"
+            android:exported="true"
+            android:label="Test Agent"
+            android:permission="android.permission.BIND_TRUST_AGENT">
+            <intent-filter>
+                <action android:name="android.service.trust.TrustAgentService" />
+            </intent-filter>
+        </service>
+
     </application>
 
     <!--  self-instrumenting test package. -->
diff --git a/tests/TrustTests/src/android/trust/test/UnlockAttemptTest.kt b/tests/TrustTests/src/android/trust/test/UnlockAttemptTest.kt
new file mode 100644
index 0000000..2c9361d
--- /dev/null
+++ b/tests/TrustTests/src/android/trust/test/UnlockAttemptTest.kt
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.trust.test
+
+import android.app.trust.TrustManager
+import android.content.Context
+import android.trust.BaseTrustAgentService
+import android.trust.TrustTestActivity
+import android.trust.test.lib.LockStateTrackingRule
+import android.trust.test.lib.ScreenLockRule
+import android.trust.test.lib.TestTrustListener
+import android.trust.test.lib.TrustAgentRule
+import android.util.Log
+import androidx.test.core.app.ApplicationProvider.getApplicationContext
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.runner.RunWith
+
+/**
+ * Test for the impacts of reporting unlock attempts.
+ *
+ * atest TrustTests:UnlockAttemptTest
+ */
+@RunWith(AndroidJUnit4::class)
+class UnlockAttemptTest {
+    private val context = getApplicationContext<Context>()
+    private val trustManager = context.getSystemService(TrustManager::class.java) as TrustManager
+    private val userId = context.userId
+    private val activityScenarioRule = ActivityScenarioRule(TrustTestActivity::class.java)
+    private val screenLockRule = ScreenLockRule(requireStrongAuth = true)
+    private val lockStateTrackingRule = LockStateTrackingRule()
+    private val trustAgentRule =
+        TrustAgentRule<UnlockAttemptTrustAgent>(startUnlocked = false, startEnabled = false)
+
+    private val trustListener = UnlockAttemptTrustListener()
+    private val agent get() = trustAgentRule.agent
+
+    @get:Rule
+    val rule: RuleChain =
+        RuleChain.outerRule(activityScenarioRule)
+            .around(screenLockRule)
+            .around(lockStateTrackingRule)
+            .around(trustAgentRule)
+
+    @Before
+    fun setUp() {
+        trustManager.registerTrustListener(trustListener)
+    }
+
+    @Test
+    fun successfulUnlockAttempt_allowsTrustAgentToStart() =
+        runUnlockAttemptTest(enableAndVerifyTrustAgent = false, managingTrust = false) {
+            trustAgentRule.enableTrustAgent()
+
+            triggerSuccessfulUnlock()
+
+            trustAgentRule.verifyAgentIsRunning(MAX_WAIT_FOR_ENABLED_TRUST_AGENT_TO_START)
+        }
+
+    @Test
+    fun successfulUnlockAttempt_notifiesTrustAgent() =
+        runUnlockAttemptTest(enableAndVerifyTrustAgent = true, managingTrust = true) {
+            val oldSuccessfulCount = agent.successfulUnlockCallCount
+            val oldFailedCount = agent.failedUnlockCallCount
+
+            triggerSuccessfulUnlock()
+
+            assertThat(agent.successfulUnlockCallCount).isEqualTo(oldSuccessfulCount + 1)
+            assertThat(agent.failedUnlockCallCount).isEqualTo(oldFailedCount)
+        }
+
+    @Test
+    fun successfulUnlockAttempt_notifiesTrustListenerOfManagedTrust() =
+        runUnlockAttemptTest(enableAndVerifyTrustAgent = true, managingTrust = true) {
+            val oldTrustManagedChangedCount = trustListener.onTrustManagedChangedCount[userId] ?: 0
+
+            triggerSuccessfulUnlock()
+
+            assertThat(trustListener.onTrustManagedChangedCount[userId] ?: 0).isEqualTo(
+                oldTrustManagedChangedCount + 1
+            )
+        }
+
+    @Test
+    fun failedUnlockAttempt_doesNotAllowTrustAgentToStart() =
+        runUnlockAttemptTest(enableAndVerifyTrustAgent = false, managingTrust = false) {
+            trustAgentRule.enableTrustAgent()
+
+            triggerFailedUnlock()
+
+            trustAgentRule.ensureAgentIsNotRunning(MAX_WAIT_FOR_ENABLED_TRUST_AGENT_TO_START)
+        }
+
+    @Test
+    fun failedUnlockAttempt_notifiesTrustAgent() =
+        runUnlockAttemptTest(enableAndVerifyTrustAgent = true, managingTrust = true) {
+            val oldSuccessfulCount = agent.successfulUnlockCallCount
+            val oldFailedCount = agent.failedUnlockCallCount
+
+            triggerFailedUnlock()
+
+            assertThat(agent.successfulUnlockCallCount).isEqualTo(oldSuccessfulCount)
+            assertThat(agent.failedUnlockCallCount).isEqualTo(oldFailedCount + 1)
+        }
+
+    @Test
+    fun failedUnlockAttempt_doesNotNotifyTrustListenerOfManagedTrust() =
+        runUnlockAttemptTest(enableAndVerifyTrustAgent = true, managingTrust = true) {
+            val oldTrustManagedChangedCount = trustListener.onTrustManagedChangedCount[userId] ?: 0
+
+            triggerFailedUnlock()
+
+            assertThat(trustListener.onTrustManagedChangedCount[userId] ?: 0).isEqualTo(
+                oldTrustManagedChangedCount
+            )
+        }
+
+    private fun runUnlockAttemptTest(
+        enableAndVerifyTrustAgent: Boolean,
+        managingTrust: Boolean,
+        testBlock: () -> Unit,
+    ) {
+        if (enableAndVerifyTrustAgent) {
+            Log.i(TAG, "Triggering successful unlock")
+            triggerSuccessfulUnlock()
+            Log.i(TAG, "Enabling and waiting for trust agent")
+            trustAgentRule.enableAndVerifyTrustAgentIsRunning(
+                MAX_WAIT_FOR_ENABLED_TRUST_AGENT_TO_START
+            )
+            Log.i(TAG, "Managing trust: $managingTrust")
+            agent.setManagingTrust(managingTrust)
+            await()
+        }
+        testBlock()
+    }
+
+    private fun triggerSuccessfulUnlock() {
+        screenLockRule.successfulScreenLockAttempt()
+        trustAgentRule.reportSuccessfulUnlock()
+        await()
+    }
+
+    private fun triggerFailedUnlock() {
+        screenLockRule.failedScreenLockAttempt()
+        trustAgentRule.reportFailedUnlock()
+        await()
+    }
+
+    companion object {
+        private const val TAG = "UnlockAttemptTest"
+        private fun await(millis: Long = 500) = Thread.sleep(millis)
+        private const val MAX_WAIT_FOR_ENABLED_TRUST_AGENT_TO_START = 10000L
+    }
+}
+
+class UnlockAttemptTrustAgent : BaseTrustAgentService() {
+    var successfulUnlockCallCount: Long = 0
+        private set
+    var failedUnlockCallCount: Long = 0
+        private set
+
+    override fun onUnlockAttempt(successful: Boolean) {
+        super.onUnlockAttempt(successful)
+        if (successful) {
+            successfulUnlockCallCount++
+        } else {
+            failedUnlockCallCount++
+        }
+    }
+}
+
+private class UnlockAttemptTrustListener : TestTrustListener() {
+    var enabledTrustAgentsChangedCount = mutableMapOf<Int, Int>()
+    var onTrustManagedChangedCount = mutableMapOf<Int, Int>()
+
+    override fun onEnabledTrustAgentsChanged(userId: Int) {
+        enabledTrustAgentsChangedCount.compute(userId) { _: Int, curr: Int? ->
+            if (curr == null) 0 else curr + 1
+        }
+    }
+
+    data class TrustChangedParams(
+        val enabled: Boolean,
+        val newlyUnlocked: Boolean,
+        val userId: Int,
+        val flags: Int,
+        val trustGrantedMessages: MutableList<String>?
+    )
+
+    val onTrustChangedCalls = mutableListOf<TrustChangedParams>()
+
+    override fun onTrustChanged(
+        enabled: Boolean,
+        newlyUnlocked: Boolean,
+        userId: Int,
+        flags: Int,
+        trustGrantedMessages: MutableList<String>
+    ) {
+        onTrustChangedCalls += TrustChangedParams(
+            enabled, newlyUnlocked, userId, flags, trustGrantedMessages
+        )
+    }
+
+    override fun onTrustManagedChanged(enabled: Boolean, userId: Int) {
+        onTrustManagedChangedCount.compute(userId) { _: Int, curr: Int? ->
+            if (curr == null) 0 else curr + 1
+        }
+    }
+}
diff --git a/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt b/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt
index f1edca3..1ccdcc6 100644
--- a/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt
+++ b/tests/TrustTests/src/android/trust/test/lib/ScreenLockRule.kt
@@ -24,6 +24,8 @@
 import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
 import androidx.test.uiautomator.UiDevice
 import com.android.internal.widget.LockPatternUtils
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
 import com.android.internal.widget.LockscreenCredential
 import com.google.common.truth.Truth.assertWithMessage
 import org.junit.rules.TestRule
@@ -32,13 +34,18 @@
 
 /**
  * Sets a screen lock on the device for the duration of the test.
+ *
+ * @param requireStrongAuth Whether a strong auth is required at the beginning.
+ * If true, trust agents will not be available until the user verifies their credentials.
  */
-class ScreenLockRule : TestRule {
+class ScreenLockRule(val requireStrongAuth: Boolean = false) : TestRule {
     private val context: Context = getApplicationContext()
+    private val userId = context.userId
     private val uiDevice = UiDevice.getInstance(getInstrumentation())
     private val windowManager = checkNotNull(WindowManagerGlobal.getWindowManagerService())
     private val lockPatternUtils = LockPatternUtils(context)
     private var instantLockSavedValue = false
+    private var strongAuthSavedValue: Int = 0
 
     override fun apply(base: Statement, description: Description) = object : Statement() {
         override fun evaluate() {
@@ -46,10 +53,12 @@
             dismissKeyguard()
             setScreenLock()
             setLockOnPowerButton()
+            configureStrongAuthState()
 
             try {
                 base.evaluate()
             } finally {
+                restoreStrongAuthState()
                 removeScreenLock()
                 revertLockOnPowerButton()
                 dismissKeyguard()
@@ -57,6 +66,22 @@
         }
     }
 
+    private fun configureStrongAuthState() {
+        strongAuthSavedValue = lockPatternUtils.getStrongAuthForUser(userId)
+        if (requireStrongAuth) {
+            Log.d(TAG, "Triggering strong auth due to simulated lockdown")
+            lockPatternUtils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN, userId)
+            wait("strong auth required after lockdown") {
+                lockPatternUtils.getStrongAuthForUser(userId) ==
+                        STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN
+            }
+        }
+    }
+
+    private fun restoreStrongAuthState() {
+        lockPatternUtils.requireStrongAuth(strongAuthSavedValue, userId)
+    }
+
     private fun verifyNoScreenLockAlreadySet() {
         assertWithMessage("Screen Lock must not already be set on device")
                 .that(lockPatternUtils.isSecure(context.userId))
@@ -82,6 +107,22 @@
         }
     }
 
+    fun successfulScreenLockAttempt() {
+        lockPatternUtils.verifyCredential(LockscreenCredential.createPin(PIN), context.userId, 0)
+        lockPatternUtils.userPresent(context.userId)
+        wait("strong auth not required") {
+            lockPatternUtils.getStrongAuthForUser(context.userId) == STRONG_AUTH_NOT_REQUIRED
+        }
+    }
+
+    fun failedScreenLockAttempt() {
+        lockPatternUtils.verifyCredential(
+            LockscreenCredential.createPin(WRONG_PIN),
+            context.userId,
+            0
+        )
+    }
+
     private fun setScreenLock() {
         lockPatternUtils.setLockCredential(
                 LockscreenCredential.createPin(PIN),
@@ -121,5 +162,6 @@
     companion object {
         private const val TAG = "ScreenLockRule"
         private const val PIN = "0000"
+        private const val WRONG_PIN = "0001"
     }
 }
diff --git a/tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt b/tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt
index 18bc029..404c6d9 100644
--- a/tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt
+++ b/tests/TrustTests/src/android/trust/test/lib/TrustAgentRule.kt
@@ -20,14 +20,15 @@
 import android.content.ComponentName
 import android.content.Context
 import android.trust.BaseTrustAgentService
+import android.trust.test.lib.TrustAgentRule.Companion.invoke
 import android.util.Log
 import androidx.test.core.app.ApplicationProvider.getApplicationContext
 import com.android.internal.widget.LockPatternUtils
 import com.google.common.truth.Truth.assertWithMessage
+import kotlin.reflect.KClass
 import org.junit.rules.TestRule
 import org.junit.runner.Description
 import org.junit.runners.model.Statement
-import kotlin.reflect.KClass
 
 /**
  * Enables a trust agent and causes the system service to bind to it.
@@ -37,7 +38,9 @@
  * @constructor Creates the rule. Do not use; instead, use [invoke].
  */
 class TrustAgentRule<T : BaseTrustAgentService>(
-    private val serviceClass: KClass<T>
+    private val serviceClass: KClass<T>,
+    private val startUnlocked: Boolean,
+    private val startEnabled: Boolean,
 ) : TestRule {
     private val context: Context = getApplicationContext()
     private val trustManager = context.getSystemService(TrustManager::class.java) as TrustManager
@@ -48,11 +51,18 @@
     override fun apply(base: Statement, description: Description) = object : Statement() {
         override fun evaluate() {
             verifyTrustServiceRunning()
-            unlockDeviceWithCredential()
-            enableTrustAgent()
+            if (startUnlocked) {
+                reportSuccessfulUnlock()
+            } else {
+                Log.i(TAG, "Trust manager not starting in unlocked state")
+            }
 
             try {
-                verifyAgentIsRunning()
+                if (startEnabled) {
+                    enableAndVerifyTrustAgentIsRunning()
+                } else {
+                    Log.i(TAG, "Trust agent ${serviceClass.simpleName} not enabled")
+                }
                 base.evaluate()
             } finally {
                 disableTrustAgent()
@@ -64,12 +74,22 @@
         assertWithMessage("Trust service is not running").that(trustManager).isNotNull()
     }
 
-    private fun unlockDeviceWithCredential() {
-        Log.d(TAG, "Unlocking device with credential")
+    fun reportSuccessfulUnlock() {
+        Log.i(TAG, "Reporting successful unlock")
         trustManager.reportUnlockAttempt(true, context.userId)
     }
 
-    private fun enableTrustAgent() {
+    fun reportFailedUnlock() {
+        Log.i(TAG, "Reporting failed unlock")
+        trustManager.reportUnlockAttempt(false, context.userId)
+    }
+
+    fun enableAndVerifyTrustAgentIsRunning(maxWait: Long = 30000L) {
+        enableTrustAgent()
+        verifyAgentIsRunning(maxWait)
+    }
+
+    fun enableTrustAgent() {
         val componentName = ComponentName(context, serviceClass.java)
         val userId = context.userId
         Log.i(TAG, "Enabling trust agent ${componentName.flattenToString()} for user $userId")
@@ -79,12 +99,18 @@
         lockPatternUtils.setEnabledTrustAgents(agents, userId)
     }
 
-    private fun verifyAgentIsRunning() {
-        wait("${serviceClass.simpleName} to be running") {
+    fun verifyAgentIsRunning(maxWait: Long = 30000L) {
+        wait("${serviceClass.simpleName} to be running", maxWait) {
             BaseTrustAgentService.instance(serviceClass) != null
         }
     }
 
+    fun ensureAgentIsNotRunning(window: Long = 30000L) {
+        ensure("${serviceClass.simpleName} is not running", window) {
+            BaseTrustAgentService.instance(serviceClass) == null
+        }
+    }
+
     private fun disableTrustAgent() {
         val componentName = ComponentName(context, serviceClass.java)
         val userId = context.userId
@@ -97,13 +123,23 @@
 
     companion object {
         /**
-         * Creates a new rule for the specified agent class. Example usage:
+         * Creates a new rule for the specified agent class. Starts with the device unlocked and
+         * the trust agent enabled. Example usage:
          * ```
          *   @get:Rule val rule = TrustAgentRule<MyTestAgent>()
          * ```
+         *
+         * Also supports setting different device lock and trust agent enablement states:
+         * ```
+         *   @get:Rule val rule = TrustAgentRule<MyTestAgent>(startUnlocked = false, startEnabled = false)
+         * ```
          */
-        inline operator fun <reified T : BaseTrustAgentService> invoke() =
-            TrustAgentRule(T::class)
+        inline operator fun <reified T : BaseTrustAgentService> invoke(
+            startUnlocked: Boolean = true,
+            startEnabled: Boolean = true,
+        ) =
+            TrustAgentRule(T::class, startUnlocked, startEnabled)
+
 
         private const val TAG = "TrustAgentRule"
     }
diff --git a/tests/TrustTests/src/android/trust/test/lib/utils.kt b/tests/TrustTests/src/android/trust/test/lib/Utils.kt
similarity index 63%
rename from tests/TrustTests/src/android/trust/test/lib/utils.kt
rename to tests/TrustTests/src/android/trust/test/lib/Utils.kt
index e047202..3b32b47 100644
--- a/tests/TrustTests/src/android/trust/test/lib/utils.kt
+++ b/tests/TrustTests/src/android/trust/test/lib/Utils.kt
@@ -39,7 +39,7 @@
 ) {
     var waited = 0L
     var count = 0
-    while (!conditionFunction.invoke(count)) {
+    while (!conditionFunction(count)) {
         assertWithMessage("Condition exceeded maximum wait time of $maxWait ms: $description")
             .that(waited <= maxWait)
             .isTrue()
@@ -49,3 +49,34 @@
         Thread.sleep(rate)
     }
 }
+
+/**
+ * Ensures that [conditionFunction] is true with a failed assertion if it is not within [window]
+ * ms.
+ *
+ * The condition function can perform additional logic (for example, logging or attempting to make
+ * the condition become true).
+ *
+ * @param conditionFunction function which takes the attempt count & returns whether the condition
+ *                          is met
+ */
+internal fun ensure(
+    description: String? = null,
+    window: Long = 30000L,
+    rate: Long = 50L,
+    conditionFunction: (count: Int) -> Boolean
+) {
+    var waited = 0L
+    var count = 0
+    while (waited <= window) {
+        assertWithMessage("Condition failed within $window ms: $description").that(
+                conditionFunction(
+                    count
+                )
+            ).isTrue()
+        waited += rate
+        count++
+        Log.i(TAG, "Ensuring $description ($waited/$window) #$count")
+        Thread.sleep(rate)
+    }
+}