Merge "Rework Spatial Audio component to work as a Toggle when head tracking is unavailable" into main
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/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/notification.aconfig b/core/java/android/app/notification.aconfig
index 6edae0b..2d78317 100644
--- a/core/java/android/app/notification.aconfig
+++ b/core/java/android/app/notification.aconfig
@@ -192,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/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/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/provider/Settings.java b/core/java/android/provider/Settings.java
index f357ebf..7933212 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -19930,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/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/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/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/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/View.java b/core/java/android/view/View.java
index 95c9d7b..075ee1b 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -34061,10 +34061,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/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 496e899..fd407d6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2790,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
@@ -3346,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
@@ -4220,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) {
diff --git a/core/java/com/android/internal/compat/ChangeReporter.java b/core/java/com/android/internal/compat/ChangeReporter.java
index ded142c..d8f1309 100644
--- a/core/java/com/android/internal/compat/ChangeReporter.java
+++ b/core/java/com/android/internal/compat/ChangeReporter.java
@@ -98,7 +98,7 @@
     public void reportChange(int uid, long changeId, int state, boolean isLoggableBySdk) {
         boolean isAlreadyReported =
                 checkAndSetIsAlreadyReported(uid, new ChangeReport(changeId, state));
-        if (!isAlreadyReported) {
+        if (shouldWriteToStatsLog(isAlreadyReported)) {
             FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED, uid,
                     changeId, state, mSource);
         }
@@ -136,14 +136,16 @@
     /**
      * Returns whether the next report should be logged to FrameworkStatsLog.
      *
-     * @param uid      affected by the change
-     * @param changeId the reported change id
-     * @param state    of the reported change - enabled/disabled/only logged
+     * @param isAlreadyReported is the change already reported
      * @return true if the report should be logged
      */
     @VisibleForTesting
-    boolean shouldWriteToStatsLog(int uid, long changeId, int state) {
-        return !isAlreadyReported(uid, new ChangeReport(changeId, state));
+    boolean shouldWriteToStatsLog(boolean isAlreadyReported) {
+        // We don't log for system server
+        if (mSource == SOURCE_SYSTEM_SERVER) return false;
+
+        // Don't log if already reported
+        return !isAlreadyReported;
     }
 
     /**
@@ -160,8 +162,6 @@
             boolean isAlreadyReported, int state, boolean isLoggableBySdk) {
         // If log all bit is on, always return true.
         if (mDebugLogAll) return true;
-        // If the change has already been reported, do not write.
-        if (isAlreadyReported) return false;
 
         // If the flag is turned off or the TAG's logging is forced to debug level with
         // `adb setprop log.tag.CompatChangeReporter=DEBUG`, write to debug since the above checks
@@ -224,6 +224,19 @@
         return mReportedChanges.getOrDefault(uid, EMPTY_SET).contains(report);
     }
 
+    /**
+     * Returns whether the next report should be logged.
+     *
+     * @param uid      affected by the change
+     * @param changeId the reported change id
+     * @param state    of the reported change - enabled/disabled/only logged
+     * @return true if the report should be logged
+     */
+    @VisibleForTesting
+    boolean isAlreadyReported(int uid, long changeId, int state) {
+        return isAlreadyReported(uid, new ChangeReport(changeId, state));
+    }
+
     private void markAsReported(int uid, ChangeReport report) {
         mReportedChanges.computeIfAbsent(uid, NEW_CHANGE_REPORT_SET).add(report);
     }
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/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java b/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java
index 12a42f9..c32fd45 100644
--- a/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java
+++ b/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java
@@ -31,21 +31,21 @@
     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     @Test
-    public void testStatsLogOnce() {
+    public void testIsAlreadyReportedOnce() {
         ChangeReporter reporter = new ChangeReporter(ChangeReporter.SOURCE_UNKNOWN_SOURCE);
         int myUid = 1022, otherUid = 1023;
         long myChangeId = 500L, otherChangeId = 600L;
         int myState = ChangeReporter.STATE_ENABLED, otherState = ChangeReporter.STATE_DISABLED;
 
-        assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+        assertFalse(reporter.isAlreadyReported(myUid, myChangeId, myState));
         reporter.reportChange(myUid, myChangeId, myState);
 
         // Same report will not be logged again.
-        assertFalse(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+        assertTrue(reporter.isAlreadyReported(myUid, myChangeId, myState));
         // Other reports will be logged.
-        assertTrue(reporter.shouldWriteToStatsLog(otherUid, myChangeId, myState));
-        assertTrue(reporter.shouldWriteToStatsLog(myUid, otherChangeId, myState));
-        assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, otherState));
+        assertFalse(reporter.isAlreadyReported(otherUid, myChangeId, myState));
+        assertFalse(reporter.isAlreadyReported(myUid, otherChangeId, myState));
+        assertFalse(reporter.isAlreadyReported(myUid, myChangeId, otherState));
     }
 
     @Test
@@ -55,15 +55,15 @@
         long myChangeId = 500L;
         int myState = ChangeReporter.STATE_ENABLED;
 
-        assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+        assertFalse(reporter.isAlreadyReported(myUid, myChangeId, myState));
         reporter.reportChange(myUid, myChangeId, myState);
 
         // Same report will not be logged again.
-        assertFalse(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+        assertTrue(reporter.isAlreadyReported(myUid, myChangeId, myState));
         reporter.resetReportedChanges(myUid);
 
         // Same report will be logged again after reset.
-        assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+        assertFalse(reporter.isAlreadyReported(myUid, myChangeId, myState));
     }
 
     @Test
@@ -196,4 +196,21 @@
         // off.
         assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, myDisabledState, false));
     }
+
+    @Test
+    public void testDontLogSystemServer() {
+        ChangeReporter systemServerReporter =
+                new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
+
+        // We never log from system server, even if already reported.
+        assertFalse(systemServerReporter.shouldWriteToStatsLog(false));
+        assertFalse(systemServerReporter.shouldWriteToStatsLog(true));
+
+        ChangeReporter unknownSourceReporter =
+                new ChangeReporter(ChangeReporter.SOURCE_UNKNOWN_SOURCE);
+
+        // Otherwise we log if it's not already been reported.
+        assertTrue(unknownSourceReporter.shouldWriteToStatsLog(false));
+        assertFalse(unknownSourceReporter.shouldWriteToStatsLog(true));
+    }
 }
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/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
index 07446e7..abe9c8e 100644
--- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java
+++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java
@@ -164,7 +164,7 @@
         mActivityRule.runOnUiThread(() -> {
             mMovingView.setFrameContentVelocity(1f);
             mMovingView.invalidate();
-            runAfterDraw(() -> assertEquals(60f, mViewRoot.getLastPreferredFrameRate(), 0f));
+            runAfterDraw(() -> assertEquals(80f, mViewRoot.getLastPreferredFrameRate(), 0f));
         });
         waitForAfterDraw();
     }
@@ -190,7 +190,7 @@
             frameLayout.setFrameContentVelocity(1f);
             mMovingView.offsetTopAndBottom(100);
             frameLayout.invalidate();
-            runAfterDraw(() -> assertEquals(60f, mViewRoot.getLastPreferredFrameRate(), 0f));
+            runAfterDraw(() -> assertEquals(80f, mViewRoot.getLastPreferredFrameRate(), 0f));
         });
         waitForAfterDraw();
     }
@@ -435,7 +435,7 @@
             runAfterDraw(() -> {
                 assertEquals(FRAME_RATE_CATEGORY_LOW,
                         mViewRoot.getLastPreferredFrameRateCategory());
-                assertEquals(60f, mViewRoot.getLastPreferredFrameRate());
+                assertEquals(80f, mViewRoot.getLastPreferredFrameRate());
             });
         });
         waitForAfterDraw();
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 774b212..23dc96c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java
@@ -245,11 +245,9 @@
                             isReversedLayout,
                             parentInfo.getDisplayId(),
                             isDraggableExpandType,
-                            getContainerBackgroundColor(
-                                    primaryContainer, DEFAULT_PRIMARY_VEIL_COLOR),
-                            getContainerBackgroundColor(
-                                    secondaryContainer, DEFAULT_SECONDARY_VEIL_COLOR)
-                    ));
+                            primaryContainer,
+                            secondaryContainer)
+            );
         }
     }
 
@@ -965,8 +963,10 @@
         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
@@ -979,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;
@@ -989,8 +989,8 @@
             mIsReversedLayout = isReversedLayout;
             mDisplayId = displayId;
             mIsDraggableExpandType = isDraggableExpandType;
-            mPrimaryVeilColor = primaryVeilColor;
-            mSecondaryVeilColor = secondaryVeilColor;
+            mPrimaryContainer = primaryContainer;
+            mSecondaryContainer = secondaryContainer;
             mDividerWidthPx = getDividerWidthPx(dividerAttributes);
         }
 
@@ -1014,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(
@@ -1328,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)
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 4515187..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
@@ -166,8 +166,8 @@
                 false /* isReversedLayout */,
                 Display.DEFAULT_DISPLAY,
                 false /* isDraggableExpandType */,
-                Color.valueOf(Color.BLACK), /* primaryVeilColor */
-                Color.valueOf(Color.GRAY) /* secondaryVeilColor */
+                mockPrimaryContainer,
+                mockSecondaryContainer
         );
 
         mDividerPresenter = new DividerPresenter(
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/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/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/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/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
index cbef68e..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,26 +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;
-    };
     std::thread mThread;
-
-protected:
-    sp<MyLooper> mLooper;
 };
 
 PointerControllerTest::PointerControllerTest()
       : mPointerSprite(new NiceMock<MockSprite>),
-        mThread(&PointerControllerTest::loopThread, this),
-        mLooper(new MyLooper) {
+        mLooper(new MyLooper),
+        mThread(&PointerControllerTest::loopThread, this) {
     mSpriteController.reset(new NiceMock<MockSpriteController>(mLooper));
     mPolicy = new MockPointerControllerPolicyInterface();
 
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/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 373b3e8..c35721c 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
@@ -230,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)
@@ -243,7 +243,7 @@
     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/logging/CreateCredentialEvent.kt b/packages/CredentialManager/src/com/android/credentialmanager/logging/CreateCredentialEvent.kt
index 39f2fce..dac25fa 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/logging/CreateCredentialEvent.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/logging/CreateCredentialEvent.kt
@@ -17,7 +17,6 @@
 
 import com.android.internal.logging.UiEvent
 import com.android.internal.logging.UiEventLogger
-import com.android.internal.logging.UiEventLogger.UiEventEnum.RESERVE_NEW_UI_EVENT_ID
 
 enum class CreateCredentialEvent(private val id: Int) : UiEventLogger.UiEventEnum {
 
@@ -56,7 +55,7 @@
     CREDMAN_CREATE_CRED_MORE_ABOUT_PASSKEYS_INTRO(1328),
 
     @UiEvent(doc = "The single tap biometric flow is launched.")
-    CREDMAN_CREATE_CRED_BIOMETRIC_FLOW_LAUNCHED(RESERVE_NEW_UI_EVENT_ID);
+    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 89fd72c..8870f28 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/logging/GetCredentialEvent.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/logging/GetCredentialEvent.kt
@@ -17,7 +17,6 @@
 
 import com.android.internal.logging.UiEvent
 import com.android.internal.logging.UiEventLogger
-import com.android.internal.logging.UiEventLogger.UiEventEnum.RESERVE_NEW_UI_EVENT_ID
 
 enum class GetCredentialEvent(private val id: Int) : UiEventLogger.UiEventEnum {
 
@@ -58,7 +57,7 @@
     CREDMAN_GET_CRED_ALL_SIGN_IN_OPTION_CARD(1342),
 
     @UiEvent(doc = "The single tap biometric flow is launched.")
-    CREDMAN_GET_CRED_BIOMETRIC_FLOW_LAUNCHED(RESERVE_NEW_UI_EVENT_ID);
+    CREDMAN_GET_CRED_BIOMETRIC_FLOW_LAUNCHED(1801);
 
     override fun getId(): Int {
         return this.id
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/SettingsLib/Color/res/values/colors.xml b/packages/SettingsLib/Color/res/values/colors.xml
index b0b9b10..7097523 100644
--- a/packages/SettingsLib/Color/res/values/colors.xml
+++ b/packages/SettingsLib/Color/res/values/colors.xml
@@ -62,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/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java
index 0447ef8..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(
@@ -67,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/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/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/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/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..7a098ee 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -78,9 +78,42 @@
     visibility: ["//visibility:private"],
 }
 
+// Tests where robolectric conversion caused errors in SystemUITests at runtime
+filegroup {
+    name: "SystemUI-tests-broken-robofiles-sysui-run",
+    srcs: [
+        "tests/src/**/systemui/globalactions/GlobalActionsDialogLiteTest.java",
+        "tests/src/**/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt",
+        "tests/src/**/systemui/media/controls/domain/pipeline/MediaDataProcessorTest.kt",
+        "tests/src/**/systemui/media/dialog/MediaOutputAdapterTest.java",
+        "tests/src/**/systemui/media/dialog/MediaOutputBaseDialogTest.java",
+        "tests/src/**/systemui/media/dialog/MediaOutputBroadcastDialogTest.java",
+        "tests/src/**/systemui/media/dialog/MediaOutputDialogTest.java",
+        "tests/src/**/systemui/mediaprojection/permission/MediaProjectionPermissionDialogDelegateTest.kt",
+    ],
+}
+
 filegroup {
     name: "SystemUI-tests-broken-robofiles-run",
     srcs: [
+        "tests/src/**/systemui/keyguard/CustomizationProviderTest.kt",
+        "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/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/util/LifecycleFragmentTest.java",
         "tests/src/**/systemui/util/TestableAlertDialogTest.kt",
         "tests/src/**/systemui/util/kotlin/PairwiseFlowTest",
@@ -767,6 +800,7 @@
         ":SystemUI-tests-robofiles",
     ],
     exclude_srcs: [
+        ":SystemUI-tests-broken-robofiles-sysui-run",
         ":SystemUI-tests-broken-robofiles-compile",
         ":SystemUI-tests-broken-robofiles-run",
     ],
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 63a52d6..3310cfa 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -736,16 +736,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"
@@ -1011,6 +1001,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"
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 a730206..d776e2a 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,10 +82,7 @@
         Scrim(onClicked = viewModel::onScrimClicked)
 
         Row(
-            modifier =
-                Modifier.fillMaxSize().thenIf(!isPanelFullWidth) {
-                    Modifier.padding(OverlayShade.Dimensions.ScrimContentPadding)
-                },
+            modifier = Modifier.fillMaxSize().panelPadding(),
             horizontalArrangement = horizontalArrangement,
         ) {
             Panel(
@@ -138,6 +144,42 @@
     )
 }
 
+@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)
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 5e19a41..a1bad39 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
@@ -54,6 +54,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
@@ -62,6 +64,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
@@ -71,7 +75,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
@@ -85,6 +88,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
 
@@ -138,6 +142,8 @@
         )
         whenever(providerInfo.profile).thenReturn(UserHandle(MAIN_USER_INFO.id))
 
+        kosmos.powerInteractor.setAwakeForTest()
+
         underTest =
             CommunalViewModel(
                 testScope,
@@ -468,6 +474,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/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/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/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/volume/domain/interactor/AudioOutputInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
index b83b93b..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
@@ -50,6 +50,8 @@
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.whenever
 
+private const val builtInDeviceName = "This phone"
+
 @OptIn(ExperimentalCoroutinesApi::class)
 @RunWith(AndroidJUnit4::class)
 @SmallTest
@@ -71,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)
             }
         }
     }
@@ -90,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)
             }
         }
     }
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/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/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/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/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/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index 3d9e861..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
@@ -91,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/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index 7f3a2dc..ce69ee8 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
@@ -38,7 +38,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
@@ -76,8 +78,11 @@
 
     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) {
@@ -93,9 +98,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() }
diff --git a/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java b/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java
index 3cf22b1..a679bfb 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/OpenHubComplication.java
@@ -67,7 +67,7 @@
     @Override
     public int getRequiredTypeAvailability() {
         // TODO(b/339667383): create a new complication type if we decide to productionize this
-        return COMPLICATION_TYPE_HOME_CONTROLS;
+        return COMPLICATION_TYPE_NONE;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 96e708f..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
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 f4f8796..3d3584e 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -60,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
@@ -386,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")
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/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/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index c44a40f..b142b44 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(
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 6d579f3..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
@@ -40,6 +40,7 @@
         rebuildSections: List<KeyguardSection> = listOf(),
         bindData: Boolean = true
     ) {
+        rebuildSections.forEach { it.onRebuildBegin() }
         val prevSections = previousBlueprint?.sections ?: listOf()
         val skipSections = sections.intersect(prevSections).subtract(rebuildSections)
         prevSections.subtract(skipSections).forEach { it.removeViews(constraintLayout) }
@@ -49,6 +50,7 @@
                 it.bindData(constraintLayout)
             }
         }
+        rebuildSections.forEach { it.onRebuildEnd() }
     }
 
     /** Rebuilds views for the target sections, or all of them if unspecified. */
@@ -61,6 +63,7 @@
             return
         }
 
+        rebuildSections.forEach { it.onRebuildBegin() }
         rebuildSections.forEach { it.removeViews(constraintLayout) }
         rebuildSections.forEach {
             it.addViews(constraintLayout)
@@ -68,6 +71,7 @@
                 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/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index c5fab8f..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()),
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 2d6690f..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
@@ -56,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/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/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index 8316b3a..7bacdeb 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,
@@ -387,12 +389,12 @@
             repeatOnLifecycle(Lifecycle.State.STARTED) {
                 listenForAnyStateToGoneKeyguardTransition(this)
                 listenForAnyStateToLockscreenTransition(this)
-                listenForLockscreenSettingChanges(this)
 
                 if (!mediaFlags.isSceneContainerEnabled()) return@repeatOnLifecycle
                 listenForMediaItemsChanges(this)
             }
         }
+        listenForLockscreenSettingChanges(applicationScope)
 
         // Notifies all active players about animation scale changes.
         globalSettings.registerContentObserver(
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 2dc09e5..41cd2c4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -500,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(
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 1143c30..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
@@ -395,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]
+                },
+            )
         }
     }
 
@@ -541,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) {
@@ -675,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/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/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/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/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/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 99ce454..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.
      */
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/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/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/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
index b448d85..fc29eab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt
@@ -413,6 +413,7 @@
         afterKeyguardGone: Boolean,
         customMessage: String?,
     ) {
+        Log.i(TAG, "Invoking dismissKeyguardThenExecute, afterKeyguardGone: $afterKeyguardGone")
         if (
             !action.willRunAnimationOnKeyguard() &&
                 wakefulnessLifecycle.wakefulness == WakefulnessLifecycle.WAKEFULNESS_ASLEEP &&
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 ebb62ec..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();
     }
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 4325897..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
@@ -35,6 +36,7 @@
 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
@@ -162,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
@@ -269,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(
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/volume/domain/interactor/AudioOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
index e0f64b4..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,15 +17,18 @@
 package com.android.systemui.volume.domain.interactor
 
 import android.bluetooth.BluetoothAdapter
+import android.content.Context
 import android.media.AudioDeviceInfo
 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.domain.interactor.MediaOutputInteractor
@@ -36,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
@@ -47,6 +51,7 @@
 class AudioOutputInteractor
 @Inject
 constructor(
+    @Application private val context: Context,
     audioRepository: AudioRepository,
     audioModeInteractor: AudioModeInteractor,
     @VolumePanelScope scope: CoroutineScope,
@@ -58,7 +63,7 @@
     audioSharingRepository: AudioSharingRepository,
 ) {
 
-    val currentAudioDevice: Flow<AudioOutputDevice> =
+    val currentAudioDevice: StateFlow<AudioOutputDevice> =
         audioModeInteractor.isOngoingCall
             .flatMapLatest { isOngoingCall ->
                 if (isOngoingCall) {
@@ -102,7 +107,7 @@
             )
         }
         return AudioOutputDevice.BuiltIn(
-            name = productName.toString(),
+            name = PhoneMediaDevice.getMediaTransferThisDeviceName(context),
             icon = deviceIconInteractor.loadIcon(type),
         )
     }
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/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/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/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/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/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/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 14d9548..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() {
 
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 ced3526..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() {
 
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 344e0fc..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
@@ -54,7 +54,7 @@
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @RunWithLooper(setAsMainLooper = true)
 @ExperimentalCoroutinesApi
 @SmallTest
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 ec2a1d3..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
@@ -18,6 +18,7 @@
 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
@@ -25,12 +26,11 @@
 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
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 c1e3e84..16dd9af 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
@@ -76,7 +77,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
@@ -84,7 +84,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(JUnit4::class)
+@RunWith(AndroidJUnit4::class)
 class KeyguardQuickAffordancesCombinedViewModelTest : SysuiTestCase() {
 
     @Mock private lateinit var activityStarter: ActivityStarter
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/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/MediaDeviceManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManagerTest.kt
index 16d8819..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
@@ -31,8 +31,8 @@
 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
@@ -86,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()
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..3ca5b42 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,
@@ -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 20fb701..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
@@ -81,7 +81,7 @@
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 class MediaHierarchyManagerTest : SysuiTestCase() {
 
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 80ebe56..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()
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/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/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/data/repository/MediaProjectionManagerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
index e487780..b7fefc0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt
@@ -17,8 +17,8 @@
 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
@@ -34,7 +34,7 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class MediaProjectionManagerRepositoryTest : SysuiTestCase() {
 
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/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 ad18099..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
@@ -43,7 +43,7 @@
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.verify
 
-@RunWith(AndroidTestingRunner::class)
+@RunWith(AndroidJUnit4::class)
 @SmallTest
 class TaskSwitcherNotificationCoordinatorTest : SysuiTestCase() {
 
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/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
index db11c3e..196f654 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,6 +19,8 @@
 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
@@ -28,11 +30,13 @@
 import android.view.accessibility.AccessibilityNodeInfo
 import android.widget.TextView
 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
@@ -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/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/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/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index a355cd1..62bffdd 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
@@ -140,7 +140,7 @@
                 mSmartReplyStateInflater,
                 mNotifLayoutInflaterFactoryProvider,
                 mHeadsUpStyleProvider,
-                mock(NotificationContentInflaterLogger.class));
+                mock(NotificationRowContentBinderLogger.class));
     }
 
     @Test
@@ -265,7 +265,7 @@
                                 R.layout.custom_view_dark);
                     }
                 },
-                mock(NotificationContentInflaterLogger.class));
+                mock(NotificationRowContentBinderLogger.class));
         assertTrue(countDownLatch.await(500, TimeUnit.MILLISECONDS));
     }
 
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/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/prod/DeviceBasedSatelliteRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
index 02f53b6..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
@@ -327,7 +328,6 @@
     @Test
     fun satelliteNotSupported_listenersAreNotRegistered() =
         testScope.runTest {
-            setupDefaultRepo()
             // GIVEN satellite is not supported
             setUpRepo(
                 uptime = MIN_UPTIME,
@@ -345,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
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/volume/domain/interactor/AudioOutputInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt
index 3f51a79..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,6 +16,7 @@
 
 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
@@ -27,6 +28,7 @@
 val Kosmos.audioOutputInteractor by
     Kosmos.Fixture {
         AudioOutputInteractor(
+            applicationContext,
             audioRepository,
             audioModeInteractor,
             testScope.backgroundScope,
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/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/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/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 77decb6..7342b00 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -1748,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
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index fe08338..d09cb3e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -5272,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;
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 0d0c21d..30e4a3e 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -34,6 +34,7 @@
 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;
@@ -334,6 +335,12 @@
             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 3fbd856..d09d7e6 100644
--- a/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java
@@ -347,8 +347,6 @@
      * 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 757abd9..29e8095 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.enforceCallerCanManageAssociationsForPackage;
+import static com.android.server.companion.utils.PermissionsUtils.checkCallerCanManageAssociationsForPackage;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -457,10 +457,6 @@
 
     /**
      * 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) {
@@ -470,9 +466,13 @@
                     "getAssociationWithCallerChecks() Association id=[" + associationId
                             + "] doesn't exist.");
         }
-        enforceCallerCanManageAssociationsForPackage(mContext, association.getUserId(),
-                association.getPackageName(), null);
-        return association;
+        if (checkCallerCanManageAssociationsForPackage(mContext, association.getUserId(),
+                association.getPackageName())) {
+            return association;
+        }
+
+        throw new IllegalArgumentException(
+                "The caller can't interact with the association id=[" + associationId + "].");
     }
 
     /**
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 6f0baef..8c1116b 100644
--- a/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java
+++ b/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java
@@ -98,6 +98,7 @@
         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 00e049c..026d29c 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -122,6 +122,7 @@
      */
     public boolean isPermissionTransferUserConsented(int associationId) {
         mAssociationStore.getAssociationWithCallerChecks(associationId);
+
         PermissionSyncRequest request = getPermissionSyncRequest(associationId);
         if (request == null) {
             return false;
@@ -146,12 +147,12 @@
             return null;
         }
 
-        Slog.i(LOG_TAG, "Creating permission sync intent for userId [" + userId
-                + "] associationId [" + associationId + "]");
-
         final AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(
                 associationId);
 
+        Slog.i(LOG_TAG, "Creating permission sync intent for userId [" + userId
+                + "] associationId [" + associationId + "]");
+
         // Create an internal intent to launch the user consent dialog
         final Bundle extras = new Bundle();
         PermissionSyncRequest request = new PermissionSyncRequest(associationId);
@@ -219,9 +220,7 @@
      * Enable perm sync for the association
      */
     public void enablePermissionsSync(int associationId) {
-        AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(
-                associationId);
-        int userId = association.getUserId();
+        int userId = mAssociationStore.getAssociationWithCallerChecks(associationId).getUserId();
         PermissionSyncRequest request = new PermissionSyncRequest(associationId);
         request.setUserConsented(true);
         mSystemDataTransferRequestStore.writeRequest(userId, request);
@@ -231,9 +230,7 @@
      * Disable perm sync for the association
      */
     public void disablePermissionsSync(int associationId) {
-        AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(
-                associationId);
-        int userId = association.getUserId();
+        int userId = mAssociationStore.getAssociationWithCallerChecks(associationId).getUserId();
         PermissionSyncRequest request = new PermissionSyncRequest(associationId);
         request.setUserConsented(false);
         mSystemDataTransferRequestStore.writeRequest(userId, request);
@@ -244,9 +241,8 @@
      */
     @Nullable
     public PermissionSyncRequest getPermissionSyncRequest(int associationId) {
-        AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(
-                associationId);
-        int userId = association.getUserId();
+        int userId = mAssociationStore.getAssociationWithCallerChecks(associationId)
+                .getUserId();
         List<SystemDataTransferRequest> requests =
                 mSystemDataTransferRequestStore.readRequestsByAssociationId(userId,
                         associationId);
@@ -263,9 +259,7 @@
      */
     public void removePermissionSyncRequest(int associationId) {
         Binder.withCleanCallingIdentity(() -> {
-            AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(
-                    associationId);
-            int userId = association.getUserId();
+            int userId = mAssociationStore.getAssociationById(associationId).getUserId();
             mSystemDataTransferRequestStore.removeRequestsByAssociationId(userId, associationId);
         });
     }
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 796d285..f397814 100644
--- a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
+++ b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java
@@ -149,6 +149,21 @@
     }
 
     /**
+     * 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.
      */
@@ -169,30 +184,21 @@
     }
 
     /**
+     * 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) {
-        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;
-        }
+        if (checkCallerCanManageAssociationsForPackage(context, userId, packageName)) return;
 
         throw new SecurityException("Caller (uid=" + getCallingUid() + ") does not have "
                 + "permissions to "
@@ -213,6 +219,25 @@
         }
     }
 
+    /**
+     * 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/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/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/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 d4c0b01..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;
@@ -4972,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/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index b97053b..8d71c70 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1391,6 +1391,7 @@
         // request changes.
         final boolean wasShortTermModelActive =
                 mAutomaticBrightnessStrategy.isShortTermModelActive();
+        boolean userInitiatedChange = displayBrightnessState.isUserInitiatedChange();
         boolean allowAutoBrightnessWhileDozing =
                 mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig();
         if (mFlags.offloadControlsDozeAutoBrightness() && mFlags.isDisplayOffloadEnabled()
@@ -1413,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/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 f923cbc9..f56d803 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -353,7 +353,7 @@
     }
 
     /**
-     * @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();
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/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 7513c40..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();
         }
     }
@@ -3416,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(),
@@ -3431,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,
@@ -4285,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;
@@ -5475,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) {
@@ -5566,6 +5593,7 @@
             }
         }
 
+        @ImfLockFree
         @Override
         public void registerInputMethodListListener(InputMethodListListener listener) {
             mInputMethodListListeners.addIfAbsent(listener);
@@ -5613,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)
@@ -5695,6 +5725,7 @@
             }
         }
 
+        @ImfLockFree
         @Override
         public void maybeFinishStylusHandwriting() {
             mHandler.removeMessages(MSG_FINISH_HANDWRITING);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index b3ab229..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() {
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/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/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/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/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/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index f4b61e7..04db3e8 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -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
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 207707e..ac2c886 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -28,6 +28,7 @@
 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;
 
@@ -385,6 +386,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(
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/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/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/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/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/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/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/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/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/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index a7dbecb..1d01420 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -794,8 +794,9 @@
                 if (curService != null && !curService.isEmpty()) {
                     try {
                         serviceComponent = ComponentName.unflattenFromString(curService);
-                        serviceInfo = getValidVoiceInteractionServiceInfo(serviceComponent);
-                    } catch (RuntimeException e) {
+                        serviceInfo = AppGlobals.getPackageManager()
+                                .getServiceInfo(serviceComponent, 0, mCurUser);
+                    } catch (RuntimeException | RemoteException e) {
                         Slog.wtf(TAG, "Bad voice interaction service name " + curService, e);
                         serviceComponent = null;
                         serviceInfo = null;
@@ -833,27 +834,6 @@
             }
         }
 
-        @Nullable
-        private ServiceInfo getValidVoiceInteractionServiceInfo(
-                @Nullable ComponentName serviceComponent) {
-            if (serviceComponent == null) {
-                return null;
-            }
-            List<ResolveInfo> services = queryInteractorServices(
-                    mCurUser, serviceComponent.getPackageName());
-            for (int i = 0; i < services.size(); i++) {
-                ResolveInfo service = services.get(i);
-                VoiceInteractionServiceInfo info = new VoiceInteractionServiceInfo(
-                        mContext.getPackageManager(), service.serviceInfo);
-                ServiceInfo candidateInfo = info.getServiceInfo();
-                if (candidateInfo != null
-                        && candidateInfo.getComponentName().equals(serviceComponent)) {
-                    return candidateInfo;
-                }
-            }
-            return null;
-        }
-
         private List<ResolveInfo> queryInteractorServices(
                 @UserIdInt int user,
                 @Nullable String packageName) {
diff --git a/telephony/java/android/telephony/satellite/SystemSelectionSpecifier.aidl b/telephony/java/android/telephony/satellite/SystemSelectionSpecifier.aidl
new file mode 100644
index 0000000..ecd248c
--- /dev/null
+++ b/telephony/java/android/telephony/satellite/SystemSelectionSpecifier.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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.telephony.satellite;
+
+ 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/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
     }