Merge "Use the right packagename for Wear SystemUI in FlagsManager" into main
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.java b/core/java/android/app/Notification.java
index 329fb00..fc3bb02 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -114,7 +114,7 @@
 import com.android.internal.graphics.ColorUtils;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.ContrastColorUtil;
-import com.android.internal.util.NewlineNormalizer;
+import com.android.internal.util.NotificationBigTextNormalizer;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -3262,12 +3262,12 @@
         return cs.toString();
     }
 
-    private static CharSequence cleanUpNewLines(@Nullable CharSequence charSequence) {
+    private static CharSequence normalizeBigText(@Nullable CharSequence charSequence) {
         if (charSequence == null) {
             return charSequence;
         }
 
-        return NewlineNormalizer.normalizeNewlines(charSequence.toString());
+        return NotificationBigTextNormalizer.normalizeBigText(charSequence.toString());
     }
 
     private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
@@ -8566,7 +8566,7 @@
             // Replace the text with the big text, but only if the big text is not empty.
             CharSequence bigTextText = mBuilder.processLegacyText(mBigText);
             if (Flags.cleanUpSpansAndNewLines()) {
-                bigTextText = cleanUpNewLines(stripStyling(bigTextText));
+                bigTextText = normalizeBigText(stripStyling(bigTextText));
             }
             if (!TextUtils.isEmpty(bigTextText)) {
                 p.text(bigTextText);
diff --git a/core/java/android/app/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/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index ed5d662..1e781532 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -64,3 +64,13 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    namespace: "virtual_devices"
+    name: "intent_interception_action_matching_fix"
+    description: "Do not match intents without actions if the filter has actions"
+    bug: "343805037"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/core/java/android/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/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/android/window/ClientWindowFrames.java b/core/java/android/window/ClientWindowFrames.java
index 1bd921b..d5398e6 100644
--- a/core/java/android/window/ClientWindowFrames.java
+++ b/core/java/android/window/ClientWindowFrames.java
@@ -56,7 +56,16 @@
     public ClientWindowFrames() {
     }
 
-    public ClientWindowFrames(ClientWindowFrames other) {
+    public ClientWindowFrames(@NonNull ClientWindowFrames other) {
+        setTo(other);
+    }
+
+    private ClientWindowFrames(@NonNull Parcel in) {
+        readFromParcel(in);
+    }
+
+    /** Updates the current frames to the given frames. */
+    public void setTo(@NonNull ClientWindowFrames other) {
         frame.set(other.frame);
         displayFrame.set(other.displayFrame);
         parentFrame.set(other.parentFrame);
@@ -67,10 +76,6 @@
         compatScale = other.compatScale;
     }
 
-    private ClientWindowFrames(Parcel in) {
-        readFromParcel(in);
-    }
-
     /** Needed for AIDL out parameters. */
     public void readFromParcel(Parcel in) {
         frame.readFromParcel(in);
diff --git a/core/java/com/android/internal/util/NewlineNormalizer.java b/core/java/com/android/internal/util/NewlineNormalizer.java
deleted file mode 100644
index 0104d1f..0000000
--- a/core/java/com/android/internal/util/NewlineNormalizer.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.util;
-
-
-import java.util.regex.Pattern;
-
-/**
- * Utility class that replaces consecutive empty lines with single new line.
- * @hide
- */
-public class NewlineNormalizer {
-
-    private static final Pattern MULTIPLE_NEWLINES = Pattern.compile("\\v(\\s*\\v)?");
-
-    // Private constructor to prevent instantiation
-    private NewlineNormalizer() {}
-
-    /**
-     * Replaces consecutive newlines with a single newline in the input text.
-     */
-    public static String normalizeNewlines(String text) {
-        return MULTIPLE_NEWLINES.matcher(text).replaceAll("\n");
-    }
-}
diff --git a/core/java/com/android/internal/util/NotificationBigTextNormalizer.java b/core/java/com/android/internal/util/NotificationBigTextNormalizer.java
new file mode 100644
index 0000000..80d4095
--- /dev/null
+++ b/core/java/com/android/internal/util/NotificationBigTextNormalizer.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+
+import android.annotation.NonNull;
+import android.os.Trace;
+
+import java.util.regex.Pattern;
+
+/**
+ * Utility class that normalizes BigText style Notification content.
+ * @hide
+ */
+public class NotificationBigTextNormalizer {
+
+    private static final Pattern MULTIPLE_NEWLINES = Pattern.compile("\\v(\\s*\\v)?");
+    private static final Pattern HORIZONTAL_WHITESPACES = Pattern.compile("\\h+");
+
+    // Private constructor to prevent instantiation
+    private NotificationBigTextNormalizer() {}
+
+    /**
+     * Normalizes the given text by collapsing consecutive new lines into single one and cleaning
+     * up each line by removing zero-width characters, invisible formatting characters, and
+     * collapsing consecutive whitespace into single space.
+     */
+    @NonNull
+    public static String normalizeBigText(@NonNull String text) {
+        try {
+            Trace.beginSection("NotifBigTextNormalizer#normalizeBigText");
+            text = MULTIPLE_NEWLINES.matcher(text).replaceAll("\n");
+            text = HORIZONTAL_WHITESPACES.matcher(text).replaceAll(" ");
+            text = normalizeLines(text);
+            return text;
+        } finally {
+            Trace.endSection();
+        }
+    }
+
+    /**
+     * Normalizes lines in a text by removing zero-width characters, invisible formatting
+     * characters, and collapsing consecutive whitespace into single space.
+     *
+     * <p>
+     * This method processes the input text line by line. It eliminates zero-width
+     * characters (U+200B to U+200D, U+FEFF, U+034F), invisible formatting
+     * characters (U+2060 to U+2065, U+206A to U+206F, U+FFF9 to U+FFFB),
+     * and replaces any sequence of consecutive whitespace characters with a single space.
+     * </p>
+     *
+     * <p>
+     * Additionally, the method trims trailing whitespace from each line and removes any
+     * resulting empty lines.
+     * </p>
+     */
+    @NonNull
+    private static String normalizeLines(@NonNull String text) {
+        String[] lines = text.split("\n");
+        final StringBuilder textSB = new StringBuilder(text.length());
+        for (int i = 0; i < lines.length; i++) {
+            final String line = lines[i];
+            final StringBuilder lineSB = new StringBuilder(line.length());
+            boolean spaceSeen = false;
+            for (int j = 0; j < line.length(); j++) {
+                final char character = line.charAt(j);
+
+                // Skip ZERO WIDTH characters
+                if ((character >= '\u200B' && character <= '\u200D')
+                        || character == '\uFEFF' || character == '\u034F') {
+                    continue;
+                }
+                // Skip INVISIBLE_FORMATTING_CHARACTERS
+                if ((character >= '\u2060' && character <= '\u2065')
+                        || (character >= '\u206A' && character <= '\u206F')
+                        || (character >= '\uFFF9' && character <= '\uFFFB')) {
+                    continue;
+                }
+
+                if (isSpace(character)) {
+                    // eliminate consecutive spaces....
+                    if (!spaceSeen) {
+                        lineSB.append(" ");
+                    }
+                    spaceSeen = true;
+                } else {
+                    spaceSeen = false;
+                    lineSB.append(character);
+                }
+            }
+            // trim line.
+            final String currentLine = lineSB.toString().trim();
+
+            // don't add empty lines after trim.
+            if (currentLine.length() > 0) {
+                if (textSB.length() > 0) {
+                    textSB.append("\n");
+                }
+                textSB.append(currentLine);
+            }
+        }
+
+        return textSB.toString();
+    }
+
+    private static boolean isSpace(char ch) {
+        return ch != '\n' && Character.isSpaceChar(ch);
+    }
+}
diff --git a/core/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/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/core/tests/utiltests/src/com/android/internal/util/NewlineNormalizerTest.java b/core/tests/utiltests/src/com/android/internal/util/NewlineNormalizerTest.java
deleted file mode 100644
index bcdac61..0000000
--- a/core/tests/utiltests/src/com/android/internal/util/NewlineNormalizerTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.util;
-
-import static junit.framework.Assert.assertEquals;
-
-
-import android.platform.test.annotations.DisabledOnRavenwood;
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Test for {@link NewlineNormalizer}
- * @hide
- */
-@DisabledOnRavenwood(blockedBy = NewlineNormalizer.class)
-@RunWith(AndroidJUnit4.class)
-public class NewlineNormalizerTest {
-
-    @Rule
-    public final RavenwoodRule mRavenwood = new RavenwoodRule();
-
-    @Test
-    public void testEmptyInput() {
-        assertEquals("", NewlineNormalizer.normalizeNewlines(""));
-    }
-
-    @Test
-    public void testSingleNewline() {
-        assertEquals("\n", NewlineNormalizer.normalizeNewlines("\n"));
-    }
-
-    @Test
-    public void testMultipleConsecutiveNewlines() {
-        assertEquals("\n", NewlineNormalizer.normalizeNewlines("\n\n\n\n\n"));
-    }
-
-    @Test
-    public void testNewlinesWithSpacesAndTabs() {
-        String input = "Line 1\n  \n \t \n\tLine 2";
-        // Adjusted expected output to include the tab character
-        String expected = "Line 1\n\tLine 2";
-        assertEquals(expected, NewlineNormalizer.normalizeNewlines(input));
-    }
-
-    @Test
-    public void testMixedNewlineCharacters() {
-        String input = "Line 1\r\nLine 2\u000BLine 3\fLine 4\u2028Line 5\u2029Line 6";
-        String expected = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6";
-        assertEquals(expected, NewlineNormalizer.normalizeNewlines(input));
-    }
-}
diff --git a/core/tests/utiltests/src/com/android/internal/util/NotificationBigTextNormalizerTest.java b/core/tests/utiltests/src/com/android/internal/util/NotificationBigTextNormalizerTest.java
new file mode 100644
index 0000000..1f2e24a
--- /dev/null
+++ b/core/tests/utiltests/src/com/android/internal/util/NotificationBigTextNormalizerTest.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import static junit.framework.Assert.assertEquals;
+
+
+import android.platform.test.annotations.DisabledOnRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test for {@link NotificationBigTextNormalizer}
+ * @hide
+ */
+@DisabledOnRavenwood(blockedBy = NotificationBigTextNormalizer.class)
+@RunWith(AndroidJUnit4.class)
+public class NotificationBigTextNormalizerTest {
+
+    @Rule
+    public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+
+    @Test
+    public void testEmptyInput() {
+        assertEquals("", NotificationBigTextNormalizer.normalizeBigText(""));
+    }
+
+    @Test
+    public void testSingleNewline() {
+        assertEquals("", NotificationBigTextNormalizer.normalizeBigText("\n"));
+    }
+
+    @Test
+    public void testMultipleConsecutiveNewlines() {
+        assertEquals("", NotificationBigTextNormalizer.normalizeBigText("\n\n\n\n\n"));
+    }
+
+    @Test
+    public void testNewlinesWithSpacesAndTabs() {
+        String input = "Line 1\n  \n \t \n\tLine 2";
+        // Adjusted expected output to include the tab character
+        String expected = "Line 1\nLine 2";
+        assertEquals(expected, NotificationBigTextNormalizer.normalizeBigText(input));
+    }
+
+    @Test
+    public void testMixedNewlineCharacters() {
+        String input = "Line 1\r\nLine 2\u000BLine 3\fLine 4\u2028Line 5\u2029Line 6";
+        String expected = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6";
+        assertEquals(expected, NotificationBigTextNormalizer.normalizeBigText(input));
+    }
+
+    @Test
+    public void testConsecutiveSpaces() {
+        // Only spaces
+        assertEquals("This is a test.", NotificationBigTextNormalizer.normalizeBigText("This"
+                + "              is   a                         test."));
+        // Zero width characters bw spaces.
+        assertEquals("This is a test.", NotificationBigTextNormalizer.normalizeBigText("This"
+                + "\u200B \u200B \u200B \u200B \u200B \u200B \u200B \u200Bis\uFEFF \uFEFF \uFEFF"
+                + " \uFEFFa \u034F \u034F \u034F \u034F \u034F \u034Ftest."));
+
+        // Invisible formatting characters bw spaces.
+        assertEquals("This is a test.", NotificationBigTextNormalizer.normalizeBigText("This"
+                + "\u2061 \u2061 \u2061 \u2061 \u2061 \u2061 \u2061 \u2061is\u206E \u206E \u206E"
+                + " \u206Ea \uFFFB \uFFFB \uFFFB \uFFFB \uFFFB \uFFFBtest."));
+        // Non breakable spaces
+        assertEquals("This is a test.", NotificationBigTextNormalizer.normalizeBigText("This"
+                + "\u00A0\u00A0\u00A0\u00A0\u00A0\u00A0is\u2005 \u2005 \u2005"
+                + " \u2005a\u2005\u2005\u2005 \u2005\u2005\u2005test."));
+    }
+
+    @Test
+    public void testZeroWidthCharRemoval() {
+        // Test each character individually
+        char[] zeroWidthChars = { '\u200B', '\u200C', '\u200D', '\uFEFF', '\u034F' };
+
+        for (char c : zeroWidthChars) {
+            String input = "Test" + c + "string";
+            String expected = "Teststring";
+            assertEquals(expected, NotificationBigTextNormalizer.normalizeBigText(input));
+        }
+    }
+
+    @Test
+    public void testWhitespaceReplacement() {
+        assertEquals("This text has horizontal whitespace.",
+                NotificationBigTextNormalizer.normalizeBigText(
+                        "This\ttext\thas\thorizontal\twhitespace."));
+        assertEquals("This text has mixed whitespace.",
+                NotificationBigTextNormalizer.normalizeBigText(
+                        "This  text  has \u00A0 mixed\u2009whitespace."));
+        assertEquals("This text has leading and trailing whitespace.",
+                NotificationBigTextNormalizer.normalizeBigText(
+                        "\t This text has leading and trailing whitespace. \n"));
+    }
+
+    @Test
+    public void testInvisibleFormattingCharacterRemoval() {
+        // Test each character individually
+        char[] invisibleFormattingChars = {
+                '\u2060', '\u2061', '\u2062', '\u2063', '\u2064', '\u2065',
+                '\u206A', '\u206B', '\u206C', '\u206D', '\u206E', '\u206F',
+                '\uFFF9', '\uFFFA', '\uFFFB'
+        };
+
+        for (char c : invisibleFormattingChars) {
+            String input = "Test " + c + "string";
+            String expected = "Test string";
+            assertEquals(expected, NotificationBigTextNormalizer.normalizeBigText(input));
+        }
+    }
+    @Test
+    public void testNonBreakSpaceReplacement() {
+        // Test each character individually
+        char[] nonBreakSpaces = {
+                '\u00A0', '\u1680', '\u2000', '\u2001', '\u2002',
+                '\u2003', '\u2004', '\u2005', '\u2006', '\u2007',
+                '\u2008', '\u2009', '\u200A', '\u202F', '\u205F', '\u3000'
+        };
+
+        for (char c : nonBreakSpaces) {
+            String input = "Test" + c + "string";
+            String expected = "Test string";
+            assertEquals(expected, NotificationBigTextNormalizer.normalizeBigText(input));
+        }
+    }
+}
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index d359a90..3cff915 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -1149,6 +1149,8 @@
 
         /**
          * Sets the serial number used for the certificate of the generated key pair.
+         * To ensure compatibility with devices and certificate parsers, the value
+         * should be 20 bytes or shorter (see RFC 5280 section 4.1.2.2).
          *
          * <p>By default, the serial number is {@code 1}.
          */
diff --git a/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/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 b43b5f3..c35721c 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt
@@ -38,7 +38,6 @@
 import com.android.credentialmanager.model.creation.CreateOptionInfo
 import com.android.credentialmanager.model.get.CredentialEntryInfo
 import com.android.credentialmanager.model.get.ProviderInfo
-import java.lang.Exception
 
 /**
  * Aggregates common display information used for the Biometric Flow.
@@ -121,11 +120,11 @@
     getBiometricCancellationSignal: () -> CancellationSignal,
     getRequestDisplayInfo: RequestDisplayInfo? = null,
     getProviderInfoList: List<ProviderInfo>? = null,
-    getProviderDisplayInfo: ProviderDisplayInfo? = null,
-) {
+    getProviderDisplayInfo: ProviderDisplayInfo? = null
+): Boolean {
     if (getBiometricPromptState() != BiometricPromptState.INACTIVE) {
         // Screen is already up, do not re-launch
-        return
+        return false
     }
     onBiometricPromptStateChange(BiometricPromptState.PENDING)
     val biometricDisplayInfo = validateAndRetrieveBiometricGetDisplayInfo(
@@ -137,7 +136,7 @@
 
     if (biometricDisplayInfo == null) {
         onBiometricFailureFallback(BiometricFlowType.GET)
-        return
+        return false
     }
 
     val callback: BiometricPrompt.AuthenticationCallback =
@@ -146,7 +145,7 @@
             getBiometricPromptState)
 
     Log.d(TAG, "The BiometricPrompt API call begins for Get.")
-    runBiometricFlow(context, biometricDisplayInfo, callback, openMoreOptionsPage,
+    return runBiometricFlow(context, biometricDisplayInfo, callback, openMoreOptionsPage,
         onBiometricFailureFallback, BiometricFlowType.GET, onCancelFlowAndFinish,
         getBiometricCancellationSignal)
 }
@@ -169,11 +168,11 @@
     getBiometricCancellationSignal: () -> CancellationSignal,
     createRequestDisplayInfo: com.android.credentialmanager.createflow
     .RequestDisplayInfo? = null,
-    createProviderInfo: EnabledProviderInfo? = null,
-) {
+    createProviderInfo: EnabledProviderInfo? = null
+): Boolean {
     if (getBiometricPromptState() != BiometricPromptState.INACTIVE) {
         // Screen is already up, do not re-launch
-        return
+        return false
     }
     onBiometricPromptStateChange(BiometricPromptState.PENDING)
     val biometricDisplayInfo = validateAndRetrieveBiometricCreateDisplayInfo(
@@ -184,7 +183,7 @@
 
     if (biometricDisplayInfo == null) {
         onBiometricFailureFallback(BiometricFlowType.CREATE)
-        return
+        return false
     }
 
     val callback: BiometricPrompt.AuthenticationCallback =
@@ -193,7 +192,7 @@
             getBiometricPromptState)
 
     Log.d(TAG, "The BiometricPrompt API call begins for Create.")
-    runBiometricFlow(context, biometricDisplayInfo, callback, openMoreOptionsPage,
+    return runBiometricFlow(context, biometricDisplayInfo, callback, openMoreOptionsPage,
         onBiometricFailureFallback, BiometricFlowType.CREATE, onCancelFlowAndFinish,
         getBiometricCancellationSignal)
 }
@@ -206,19 +205,19 @@
  * only device credentials are requested.
  */
 private fun runBiometricFlow(
-    context: Context,
-    biometricDisplayInfo: BiometricDisplayInfo,
-    callback: BiometricPrompt.AuthenticationCallback,
-    openMoreOptionsPage: () -> Unit,
-    onBiometricFailureFallback: (BiometricFlowType) -> Unit,
-    biometricFlowType: BiometricFlowType,
-    onCancelFlowAndFinish: () -> Unit,
-    getBiometricCancellationSignal: () -> CancellationSignal,
-) {
+        context: Context,
+        biometricDisplayInfo: BiometricDisplayInfo,
+        callback: BiometricPrompt.AuthenticationCallback,
+        openMoreOptionsPage: () -> Unit,
+        onBiometricFailureFallback: (BiometricFlowType) -> Unit,
+        biometricFlowType: BiometricFlowType,
+        onCancelFlowAndFinish: () -> Unit,
+        getBiometricCancellationSignal: () -> CancellationSignal
+): Boolean {
     try {
         if (!canCallBiometricPrompt(biometricDisplayInfo, context)) {
             onBiometricFailureFallback(biometricFlowType)
-            return
+            return false
         }
 
         val biometricPrompt = setupBiometricPrompt(context, biometricDisplayInfo,
@@ -231,7 +230,7 @@
         val cryptoOpId = getCryptoOpId(biometricDisplayInfo)
         if (cryptoOpId != null) {
             biometricPrompt.authenticate(
-                BiometricPrompt.CryptoObject(cryptoOpId.toLong()),
+                BiometricPrompt.CryptoObject(cryptoOpId),
                 cancellationSignal, executor, callback)
         } else {
             biometricPrompt.authenticate(cancellationSignal, executor, callback)
@@ -239,10 +238,12 @@
     } catch (e: IllegalArgumentException) {
         Log.w(TAG, "Calling the biometric prompt API failed with: /n${e.localizedMessage}\n")
         onBiometricFailureFallback(biometricFlowType)
+        return false
     }
+    return true
 }
 
-private fun getCryptoOpId(biometricDisplayInfo: BiometricDisplayInfo): Int? {
+private fun getCryptoOpId(biometricDisplayInfo: BiometricDisplayInfo): Long? {
     return biometricDisplayInfo.biometricRequestInfo.opId
 }
 
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt
index 3c80113..cb089ad 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/Constants.kt
@@ -22,9 +22,12 @@
         const val BUNDLE_KEY_PREFER_IMMEDIATELY_AVAILABLE_CREDENTIALS =
             "androidx.credentials.BUNDLE_KEY_IS_AUTO_SELECT_ALLOWED"
         const val IS_AUTO_SELECTED_KEY = "IS_AUTO_SELECTED"
-        // TODO(b/333445772) : Qualify error codes fully for propagation
-        const val BIOMETRIC_AUTH_RESULT = "BIOMETRIC_AUTH_RESULT"
-        const val BIOMETRIC_AUTH_ERROR_CODE = "BIOMETRIC_AUTH_ERROR_CODE"
-        const val BIOMETRIC_AUTH_ERROR_MESSAGE = "BIOMETRIC_AUTH_ERROR_MESSAGE"
+        const val BIOMETRIC_AUTH_RESULT = "androidx.credentials.provider.BIOMETRIC_AUTH_RESULT"
+        const val BIOMETRIC_AUTH_ERROR_CODE =
+                "androidx.credentials.provider.BIOMETRIC_AUTH_ERROR_CODE"
+        const val BIOMETRIC_AUTH_ERROR_MESSAGE =
+                "androidx.credentials.provider.BIOMETRIC_AUTH_ERROR_MESSAGE"
+        const val BIOMETRIC_FRAMEWORK_OPTION =
+                "androidx.credentials.provider.BIOMETRIC_FRAMEWORK_OPTION"
     }
 }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
index 7d61f73..4993a1f 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt
@@ -123,7 +123,8 @@
                                 onBiometricPromptStateChange =
                                 viewModel::onBiometricPromptStateChange,
                                 getBiometricCancellationSignal =
-                                viewModel::getBiometricCancellationSignal
+                                viewModel::getBiometricCancellationSignal,
+                                onLog = { viewModel.logUiEvent(it) },
                             )
                         CreateScreenState.MORE_OPTIONS_SELECTION_ONLY -> MoreOptionsSelectionCard(
                                 requestDisplayInfo = createCredentialUiState.requestDisplayInfo,
@@ -642,12 +643,13 @@
     getBiometricPromptState: () -> BiometricPromptState,
     onBiometricPromptStateChange: (BiometricPromptState) -> Unit,
     getBiometricCancellationSignal: () -> CancellationSignal,
+    onLog: @Composable (UiEventEnum) -> Unit
 ) {
     if (biometricEntry == null) {
         fallbackToOriginalFlow(BiometricFlowType.CREATE)
         return
     }
-    runBiometricFlowForCreate(
+    val biometricFlowCalled = runBiometricFlowForCreate(
         biometricEntry = biometricEntry,
         context = LocalContext.current,
         openMoreOptionsPage = onMoreOptionSelected,
@@ -659,6 +661,9 @@
         createProviderInfo = enabledProviderInfo,
         onBiometricFailureFallback = fallbackToOriginalFlow,
         onIllegalStateAndFinish = onIllegalScreenStateAndFinish,
-        getBiometricCancellationSignal = getBiometricCancellationSignal,
+        getBiometricCancellationSignal = getBiometricCancellationSignal
     )
+    if (biometricFlowCalled) {
+        onLog(CreateCredentialEvent.CREDMAN_CREATE_CRED_BIOMETRIC_FLOW_LAUNCHED)
+    }
 }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
index ba61b90..517ad00 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -166,7 +166,8 @@
                                 onBiometricPromptStateChange =
                                 viewModel::onBiometricPromptStateChange,
                                 getBiometricCancellationSignal =
-                                viewModel::getBiometricCancellationSignal
+                                viewModel::getBiometricCancellationSignal,
+                                onLog = { viewModel.logUiEvent(it) },
                             )
                         } else if (credmanBiometricApiEnabled() &&
                                 getCredentialUiState.currentScreenState
@@ -260,12 +261,13 @@
     getBiometricPromptState: () -> BiometricPromptState,
     onBiometricPromptStateChange: (BiometricPromptState) -> Unit,
     getBiometricCancellationSignal: () -> CancellationSignal,
+    onLog: @Composable (UiEventEnum) -> Unit,
 ) {
     if (biometricEntry == null) {
         fallbackToOriginalFlow(BiometricFlowType.GET)
         return
     }
-    runBiometricFlowForGet(
+    val biometricFlowCalled = runBiometricFlowForGet(
         biometricEntry = biometricEntry,
         context = LocalContext.current,
         openMoreOptionsPage = onMoreOptionSelected,
@@ -280,6 +282,9 @@
         onBiometricFailureFallback = fallbackToOriginalFlow,
         getBiometricCancellationSignal = getBiometricCancellationSignal
     )
+    if (biometricFlowCalled) {
+        onLog(GetCredentialEvent.CREDMAN_GET_CRED_BIOMETRIC_FLOW_LAUNCHED)
+    }
 }
 
 /** Draws the primary credential selection page, used in Android U. */
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/logging/CreateCredentialEvent.kt b/packages/CredentialManager/src/com/android/credentialmanager/logging/CreateCredentialEvent.kt
index daa42be..dac25fa 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/logging/CreateCredentialEvent.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/logging/CreateCredentialEvent.kt
@@ -52,7 +52,10 @@
     CREDMAN_CREATE_CRED_EXTERNAL_ONLY_SELECTION(1327),
 
     @UiEvent(doc = "The more about passkeys intro card is visible on screen.")
-    CREDMAN_CREATE_CRED_MORE_ABOUT_PASSKEYS_INTRO(1328);
+    CREDMAN_CREATE_CRED_MORE_ABOUT_PASSKEYS_INTRO(1328),
+
+    @UiEvent(doc = "The single tap biometric flow is launched.")
+    CREDMAN_CREATE_CRED_BIOMETRIC_FLOW_LAUNCHED(1800);
 
     override fun getId(): Int {
         return this.id
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/logging/GetCredentialEvent.kt b/packages/CredentialManager/src/com/android/credentialmanager/logging/GetCredentialEvent.kt
index 8de8895..8870f28 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/logging/GetCredentialEvent.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/logging/GetCredentialEvent.kt
@@ -54,7 +54,10 @@
     CREDMAN_GET_CRED_PRIMARY_SELECTION_CARD(1341),
 
     @UiEvent(doc = "The all sign in option card is visible on screen.")
-    CREDMAN_GET_CRED_ALL_SIGN_IN_OPTION_CARD(1342);
+    CREDMAN_GET_CRED_ALL_SIGN_IN_OPTION_CARD(1342),
+
+    @UiEvent(doc = "The single tap biometric flow is launched.")
+    CREDMAN_GET_CRED_BIOMETRIC_FLOW_LAUNCHED(1801);
 
     override fun getId(): Int {
         return this.id
diff --git a/packages/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/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/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt
index ac5004e..580aba5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/HorizontalVolumePanelContent.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.volume.panel.ui.composable
 
+import androidx.compose.animation.AnimatedContent
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
@@ -56,17 +57,19 @@
                     with(component.component as ComposeVolumePanelUiComponent) { Content(Modifier) }
                 }
             }
-            Row(
-                modifier = Modifier.fillMaxWidth(),
-                horizontalArrangement = Arrangement.spacedBy(spacing),
-            ) {
-                for (component in layout.footerComponents) {
-                    AnimatedVisibility(
-                        visible = component.isVisible,
-                        modifier = Modifier.weight(1f),
-                    ) {
-                        with(component.component as ComposeVolumePanelUiComponent) {
-                            Content(Modifier)
+            AnimatedContent(
+                targetState = layout.footerComponents,
+                label = "FooterComponentAnimation",
+            ) { footerComponents ->
+                Row(
+                    modifier = Modifier.fillMaxWidth(),
+                    horizontalArrangement = Arrangement.spacedBy(spacing),
+                ) {
+                    for (component in footerComponents) {
+                        if (component.isVisible) {
+                            with(component.component as ComposeVolumePanelUiComponent) {
+                                Content(Modifier.weight(1f))
+                            }
                         }
                     }
                 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt
index 9ea20b9..6349c14 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VerticalVolumePanelContent.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.volume.panel.ui.composable
 
+import androidx.compose.animation.AnimatedContent
 import androidx.compose.animation.AnimatedVisibility
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
@@ -50,26 +51,27 @@
                 with(component.component as ComposeVolumePanelUiComponent) { Content(Modifier) }
             }
         }
-        if (layout.footerComponents.isNotEmpty()) {
+
+        AnimatedContent(
+            targetState = layout.footerComponents,
+            label = "FooterComponentAnimation",
+        ) { footerComponents ->
             Row(
                 modifier = Modifier.fillMaxWidth().wrapContentHeight(),
                 horizontalArrangement = Arrangement.spacedBy(if (isLargeScreen) 28.dp else 20.dp),
             ) {
                 val visibleComponentsCount =
-                    layout.footerComponents.fastSumBy { if (it.isVisible) 1 else 0 }
+                    footerComponents.fastSumBy { if (it.isVisible) 1 else 0 }
 
                 // Center footer component if there is only one present
                 if (visibleComponentsCount == 1) {
                     Spacer(modifier = Modifier.weight(0.5f))
                 }
 
-                for (component in layout.footerComponents) {
-                    AnimatedVisibility(
-                        visible = component.isVisible,
-                        modifier = Modifier.weight(1f),
-                    ) {
+                for (component in footerComponents) {
+                    if (component.isVisible) {
                         with(component.component as ComposeVolumePanelUiComponent) {
-                            Content(Modifier)
+                            Content(Modifier.weight(1f))
                         }
                     }
                 }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/util/Assert.java b/packages/SystemUI/customization/src/com/android/systemui/util/Assert.java
index 165e972..de9baa5 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/util/Assert.java
+++ b/packages/SystemUI/customization/src/com/android/systemui/util/Assert.java
@@ -79,6 +79,21 @@
         }
     }
 
+    /**
+     * Asserts that the current thread is the same as the given thread, or that the current thread
+     * is the test thread.
+     * @param expected The looper we expected to be running on
+     */
+    public static void isCurrentThread(Looper expected) {
+        if (!expected.isCurrentThread()
+                && (sTestThread == null || sTestThread != Thread.currentThread())) {
+            throw new IllegalStateException("Called on wrong thread thread."
+                    + " wanted " + expected.getThread().getName()
+                    + " but instead got Thread.currentThread()="
+                    + Thread.currentThread().getName());
+        }
+    }
+
     public static void isNotMainThread() {
         if (sMainLooper.isCurrentThread()
                 && (sTestThread == null || sTestThread == Thread.currentThread())) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/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/ui/navigation/VolumeNavigatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/ui/navigation/VolumeNavigatorTest.kt
new file mode 100644
index 0000000..7934b02
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/ui/navigation/VolumeNavigatorTest.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.ui.navigation
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.uiEventLoggerFake
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.domain.model.VolumePanelRoute
+import com.android.systemui.volume.panel.domain.interactor.volumePanelGlobalStateInteractor
+import com.android.systemui.volume.panel.ui.viewmodel.volumePanelViewModelFactory
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class VolumeNavigatorTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+
+    private val underTest: VolumeNavigator =
+        with(kosmos) {
+            VolumeNavigator(
+                testScope.backgroundScope,
+                testDispatcher,
+                mock {},
+                activityStarter,
+                volumePanelViewModelFactory,
+                mock {
+                    on { create(any(), anyInt(), anyBoolean(), any()) }.thenReturn(mock {})
+                    on { applicationContext }.thenReturn(context)
+                },
+                uiEventLoggerFake,
+                volumePanelGlobalStateInteractor,
+            )
+        }
+
+    @Test
+    fun showNewVolumePanel_keyguardLocked_notShown() =
+        with(kosmos) {
+            testScope.runTest {
+                val panelState by collectLastValue(volumePanelGlobalStateInteractor.globalState)
+
+                underTest.openVolumePanel(VolumePanelRoute.COMPOSE_VOLUME_PANEL)
+                runCurrent()
+
+                assertThat(panelState!!.isVisible).isFalse()
+            }
+        }
+
+    @Test
+    fun showNewVolumePanel_keyguardUnlocked_shown() =
+        with(kosmos) {
+            testScope.runTest {
+                whenever(activityStarter.dismissKeyguardThenExecute(any(), any(), anyBoolean()))
+                    .then { (it.arguments[0] as ActivityStarter.OnDismissAction).onDismiss() }
+                val panelState by collectLastValue(volumePanelGlobalStateInteractor.globalState)
+
+                underTest.openVolumePanel(VolumePanelRoute.COMPOSE_VOLUME_PANEL)
+                runCurrent()
+
+                assertThat(panelState!!.isVisible).isTrue()
+            }
+        }
+}
diff --git a/packages/SystemUI/res/layout/activity_keyboard_shortcut_helper.xml b/packages/SystemUI/res/layout/activity_keyboard_shortcut_helper.xml
index 292e496..06d1bf4 100644
--- a/packages/SystemUI/res/layout/activity_keyboard_shortcut_helper.xml
+++ b/packages/SystemUI/res/layout/activity_keyboard_shortcut_helper.xml
@@ -5,9 +5,9 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <LinearLayout
+    <FrameLayout
         android:id="@+id/shortcut_helper_sheet"
-        style="@style/Widget.Material3.BottomSheet"
+        style="@style/ShortcutHelperBottomSheet"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:orientation="vertical"
@@ -19,13 +19,9 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content" />
 
-        <TextView
+        <androidx.compose.ui.platform.ComposeView
+            android:id="@+id/shortcut_helper_compose_container"
             android:layout_width="match_parent"
-            android:layout_height="0dp"
-            android:layout_weight="1"
-            android:gravity="center"
-            android:textAppearance="?textAppearanceDisplayLarge"
-            android:background="?colorTertiaryContainer"
-            android:text="Shortcut Helper Content" />
-    </LinearLayout>
-</androidx.coordinatorlayout.widget.CoordinatorLayout>
+            android:layout_height="match_parent" />
+    </FrameLayout>
+</androidx.coordinatorlayout.widget.CoordinatorLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index c038a82..1226bbf 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3489,6 +3489,45 @@
     <!-- Label for recent app usage of a phone sensor with sub-attribution and proxy label in the privacy dialog [CHAR LIMIT=NONE] -->
     <string name="privacy_dialog_recent_app_usage_2">Recently used by <xliff:g id="app_name" example="Gmail">%1$s</xliff:g> (<xliff:g id="attribution_label" example="For Wallet">%2$s</xliff:g> \u2022 <xliff:g id="proxy_label" example="Speech services">%3$s</xliff:g>)</string>
 
+    <!-- Title of the keyboard shortcut helper category "System". The helper is a component that
+         shows the user which keyboard shortcuts they can use. The "System" shortcuts are for
+         example "Take a screenshot" or "Go back". [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_category_system">System</string>
+    <!-- Title of the keyboard shortcut helper category "Multitasking". The helper is a component
+         that shows the user which keyboard shortcuts they can use. The "Multitasking" shortcuts are
+         for example "Enter split screen". [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_category_multitasking">Multitasking</string>
+    <!-- Title of the keyboard shortcut helper category "Input". The helper is a component
+         that shows the user which keyboard shortcuts they can use. The "Input" shortcuts are
+         the ones provided by the keyboard. Examples are "Access emoji" or "Switch to next language"
+         [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_category_input">Input</string>
+    <!-- Title of the keyboard shortcut helper category "App shortcuts". The helper is a component
+         that shows the user which keyboard shortcuts they can use. The "App shortcuts" are
+         for example "Open browser" or "Open calculator". [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_category_app_shortcuts">App shortcuts</string>
+    <!-- Title of the keyboard shortcut helper category "Accessibility". The helper is a component
+         that shows the user which keyboard shortcuts they can use. The "Accessibility" shortcuts
+         are for example "Turn on talkback". [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_category_a11y">Accessibility</string>
+    <!-- Title at the top of the keyboard shortcut helper UI. The helper is a component
+         that shows the user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_title">Keyboard shortcuts</string>
+    <!-- Placeholder text shown in the search box of the keyboard shortcut helper, when the user
+         hasn't typed in anything in the search box yet. The helper is a  component that shows the
+         user which keyboard shortcuts they can use. [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_search_placeholder">Search shortcuts</string>
+    <!-- Content description of the icon that allows to collapse a keyboard shortcut helper category
+         panel. The helper is a  component that shows the  user which keyboard shortcuts they can
+         use. The helper shows shortcuts in categories, which can be collapsed or expanded.
+         [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_content_description_collapse_icon">Collapse icon</string>
+    <!-- Content description of the icon that allows to expand a keyboard shortcut helper category
+         panel. The helper is a  component that shows the  user which keyboard shortcuts they can
+         use. The helper shows shortcuts in categories, which can be collapsed or expanded.
+         [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_content_description_expand_icon">Expand icon</string>
+
     <!-- Content description for keyboard backlight brightness dialog [CHAR LIMIT=NONE] -->
     <string name="keyboard_backlight_dialog_title">Keyboard backlight</string>
     <!-- Content description for keyboard backlight brightness value [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 64717fc..1e0adec 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -1665,6 +1665,10 @@
         <item name="android:colorBackground">@color/transparent</item>
     </style>
 
+    <style name="ShortcutHelperBottomSheet" parent="@style/Widget.Material3.BottomSheet">
+        <item name="backgroundTint">?colorSurfaceContainer</item>
+    </style>
+
     <style name="ShortcutHelperAnimation" parent="@android:style/Animation.Activity">
         <item name="android:activityOpenEnterAnimation">@anim/shortcut_helper_launch_anim</item>
         <item name="android:taskOpenEnterAnimation">@anim/shortcut_helper_launch_anim</item>
diff --git a/packages/SystemUI/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/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
new file mode 100644
index 0000000..52ccc21
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut.ui.composable
+
+import androidx.annotation.StringRes
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.OpenInNew
+import androidx.compose.material.icons.filled.Accessibility
+import androidx.compose.material.icons.filled.Apps
+import androidx.compose.material.icons.filled.ExpandMore
+import androidx.compose.material.icons.filled.Keyboard
+import androidx.compose.material.icons.filled.Search
+import androidx.compose.material.icons.filled.Tv
+import androidx.compose.material.icons.filled.VerticalSplit
+import androidx.compose.material3.CenterAlignedTopAppBar
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.NavigationDrawerItemColors
+import androidx.compose.material3.NavigationDrawerItemDefaults
+import androidx.compose.material3.SearchBar
+import androidx.compose.material3.SearchBarDefaults
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.graphicsLayer
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.compose.ui.util.fastForEach
+import androidx.compose.ui.util.fastForEachIndexed
+import com.android.compose.windowsizeclass.LocalWindowSizeClass
+import com.android.systemui.res.R
+
+@Composable
+fun ShortcutHelper(modifier: Modifier = Modifier, onKeyboardSettingsClicked: () -> Unit) {
+    if (shouldUseSinglePane()) {
+        ShortcutHelperSinglePane(modifier, categories, onKeyboardSettingsClicked)
+    } else {
+        ShortcutHelperTwoPane(modifier, categories, onKeyboardSettingsClicked)
+    }
+}
+
+@Composable
+private fun shouldUseSinglePane() =
+    LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Compact
+
+@Composable
+private fun ShortcutHelperSinglePane(
+    modifier: Modifier = Modifier,
+    categories: List<ShortcutHelperCategory>,
+    onKeyboardSettingsClicked: () -> Unit,
+) {
+    Column(
+        modifier =
+            modifier
+                .fillMaxSize()
+                .verticalScroll(rememberScrollState())
+                .padding(start = 16.dp, end = 16.dp, top = 26.dp)
+    ) {
+        TitleBar()
+        Spacer(modifier = Modifier.height(6.dp))
+        ShortcutsSearchBar()
+        Spacer(modifier = Modifier.height(16.dp))
+        CategoriesPanelSinglePane(categories)
+        Spacer(modifier = Modifier.weight(1f))
+        KeyboardSettings(onClick = onKeyboardSettingsClicked)
+    }
+}
+
+@Composable
+private fun CategoriesPanelSinglePane(
+    categories: List<ShortcutHelperCategory>,
+) {
+    var expandedCategory by remember { mutableStateOf<ShortcutHelperCategory?>(null) }
+    Column(verticalArrangement = Arrangement.spacedBy(2.dp)) {
+        categories.fastForEachIndexed { index, category ->
+            val isExpanded = expandedCategory == category
+            val itemShape =
+                if (index == 0) {
+                    ShortcutHelper.Shapes.singlePaneFirstCategory
+                } else if (index == categories.lastIndex) {
+                    ShortcutHelper.Shapes.singlePaneLastCategory
+                } else {
+                    ShortcutHelper.Shapes.singlePaneCategory
+                }
+            CategoryItemSinglePane(
+                category = category,
+                isExpanded = isExpanded,
+                onClick = {
+                    expandedCategory =
+                        if (isExpanded) {
+                            null
+                        } else {
+                            category
+                        }
+                },
+                shape = itemShape,
+            )
+        }
+    }
+}
+
+@Composable
+private fun CategoryItemSinglePane(
+    category: ShortcutHelperCategory,
+    isExpanded: Boolean,
+    onClick: () -> Unit,
+    shape: Shape,
+) {
+    Surface(
+        color = MaterialTheme.colorScheme.surfaceBright,
+        shape = shape,
+        onClick = onClick,
+    ) {
+        Column {
+            Row(
+                verticalAlignment = Alignment.CenterVertically,
+                modifier = Modifier.fillMaxWidth().heightIn(min = 88.dp).padding(horizontal = 16.dp)
+            ) {
+                Icon(category.icon, contentDescription = null)
+                Spacer(modifier = Modifier.width(16.dp))
+                Text(stringResource(category.labelResId))
+                Spacer(modifier = Modifier.weight(1f))
+                RotatingExpandCollapseIcon(isExpanded)
+            }
+            AnimatedVisibility(visible = isExpanded) { ShortcutCategoryDetailsSinglePane(category) }
+        }
+    }
+}
+
+@Composable
+private fun RotatingExpandCollapseIcon(isExpanded: Boolean) {
+    val expandIconRotationDegrees by
+        animateFloatAsState(
+            targetValue =
+                if (isExpanded) {
+                    180f
+                } else {
+                    0f
+                },
+            label = "Expand icon rotation animation"
+        )
+    Icon(
+        modifier =
+            Modifier.background(
+                    color = MaterialTheme.colorScheme.surfaceContainerHigh,
+                    shape = CircleShape
+                )
+                .graphicsLayer { rotationZ = expandIconRotationDegrees },
+        imageVector = Icons.Default.ExpandMore,
+        contentDescription =
+            if (isExpanded) {
+                stringResource(R.string.shortcut_helper_content_description_collapse_icon)
+            } else {
+                stringResource(R.string.shortcut_helper_content_description_expand_icon)
+            },
+        tint = MaterialTheme.colorScheme.onSurface
+    )
+}
+
+@Composable
+private fun ShortcutCategoryDetailsSinglePane(category: ShortcutHelperCategory) {
+    Box(modifier = Modifier.fillMaxWidth().heightIn(min = 300.dp)) {
+        Text(
+            modifier = Modifier.align(Alignment.Center),
+            text = stringResource(category.labelResId),
+        )
+    }
+}
+
+@Composable
+private fun ShortcutHelperTwoPane(
+    modifier: Modifier = Modifier,
+    categories: List<ShortcutHelperCategory>,
+    onKeyboardSettingsClicked: () -> Unit,
+) {
+    Column(modifier = modifier.fillMaxSize().padding(start = 24.dp, end = 24.dp, top = 26.dp)) {
+        TitleBar()
+        Spacer(modifier = Modifier.height(12.dp))
+        Row(Modifier.fillMaxWidth()) {
+            StartSidePanel(
+                modifier = Modifier.fillMaxWidth(fraction = 0.32f),
+                categories = categories,
+                onKeyboardSettingsClicked = onKeyboardSettingsClicked,
+            )
+            Spacer(modifier = Modifier.width(24.dp))
+            EndSidePanel(Modifier.fillMaxSize())
+        }
+    }
+}
+
+@Composable
+private fun StartSidePanel(
+    modifier: Modifier,
+    categories: List<ShortcutHelperCategory>,
+    onKeyboardSettingsClicked: () -> Unit,
+) {
+    Column(modifier) {
+        ShortcutsSearchBar()
+        Spacer(modifier = Modifier.heightIn(16.dp))
+        CategoriesPanelTwoPane(categories)
+        Spacer(modifier = Modifier.weight(1f))
+        KeyboardSettings(onKeyboardSettingsClicked)
+    }
+}
+
+@Composable
+private fun CategoriesPanelTwoPane(categories: List<ShortcutHelperCategory>) {
+    var selected by remember { mutableStateOf(categories.first()) }
+    Column {
+        categories.fastForEach {
+            CategoryItemTwoPane(
+                label = stringResource(it.labelResId),
+                icon = it.icon,
+                selected = selected == it,
+                onClick = { selected = it }
+            )
+        }
+    }
+}
+
+@Composable
+private fun CategoryItemTwoPane(
+    label: String,
+    icon: ImageVector,
+    selected: Boolean,
+    onClick: () -> Unit,
+    colors: NavigationDrawerItemColors =
+        NavigationDrawerItemDefaults.colors(unselectedContainerColor = Color.Transparent),
+) {
+    Surface(
+        selected = selected,
+        onClick = onClick,
+        modifier = Modifier.semantics { role = Role.Tab }.heightIn(min = 72.dp).fillMaxWidth(),
+        shape = RoundedCornerShape(28.dp),
+        color = colors.containerColor(selected).value,
+    ) {
+        Row(Modifier.padding(horizontal = 24.dp), verticalAlignment = Alignment.CenterVertically) {
+            Icon(
+                modifier = Modifier.size(24.dp),
+                imageVector = icon,
+                contentDescription = null,
+                tint = colors.iconColor(selected).value
+            )
+            Spacer(Modifier.width(12.dp))
+            Box(Modifier.weight(1f)) {
+                Text(
+                    fontSize = 18.sp,
+                    color = colors.textColor(selected).value,
+                    style = MaterialTheme.typography.headlineSmall,
+                    text = label
+                )
+            }
+        }
+    }
+}
+
+@Composable
+fun EndSidePanel(modifier: Modifier) {
+    Surface(
+        modifier = modifier,
+        shape = RoundedCornerShape(28.dp),
+        color = MaterialTheme.colorScheme.surfaceBright
+    ) {}
+}
+
+@Composable
+@OptIn(ExperimentalMaterial3Api::class)
+private fun TitleBar() {
+    CenterAlignedTopAppBar(
+        colors = TopAppBarDefaults.centerAlignedTopAppBarColors(containerColor = Color.Transparent),
+        title = {
+            Text(
+                text = stringResource(R.string.shortcut_helper_title),
+                color = MaterialTheme.colorScheme.onSurface,
+                style = MaterialTheme.typography.headlineSmall
+            )
+        }
+    )
+}
+
+@Composable
+@OptIn(ExperimentalMaterial3Api::class)
+private fun ShortcutsSearchBar() {
+    var query by remember { mutableStateOf("") }
+    SearchBar(
+        colors = SearchBarDefaults.colors(containerColor = MaterialTheme.colorScheme.surfaceBright),
+        query = query,
+        active = false,
+        onActiveChange = {},
+        onQueryChange = { query = it },
+        onSearch = {},
+        leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) },
+        placeholder = { Text(text = stringResource(R.string.shortcut_helper_search_placeholder)) },
+        content = {}
+    )
+}
+
+@Composable
+private fun KeyboardSettings(onClick: () -> Unit) {
+    Surface(
+        onClick = onClick,
+        shape = RoundedCornerShape(24.dp),
+        color = Color.Transparent,
+        modifier = Modifier.semantics { role = Role.Button }.fillMaxWidth()
+    ) {
+        Row(
+            modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp),
+            verticalAlignment = Alignment.CenterVertically
+        ) {
+            Text(
+                "Keyboard Settings",
+                color = MaterialTheme.colorScheme.onSurfaceVariant,
+                fontSize = 16.sp
+            )
+            Spacer(modifier = Modifier.width(8.dp))
+            Icon(
+                imageVector = Icons.AutoMirrored.Default.OpenInNew,
+                contentDescription = null,
+                tint = MaterialTheme.colorScheme.onSurfaceVariant
+            )
+        }
+    }
+}
+
+/** Temporary data class just to populate the UI. */
+private data class ShortcutHelperCategory(
+    @StringRes val labelResId: Int,
+    val icon: ImageVector,
+)
+
+// Temporarily populating the categories directly in the UI.
+private val categories =
+    listOf(
+        ShortcutHelperCategory(R.string.shortcut_helper_category_system, Icons.Default.Tv),
+        ShortcutHelperCategory(
+            R.string.shortcut_helper_category_multitasking,
+            Icons.Default.VerticalSplit
+        ),
+        ShortcutHelperCategory(R.string.shortcut_helper_category_input, Icons.Default.Keyboard),
+        ShortcutHelperCategory(R.string.shortcut_helper_category_app_shortcuts, Icons.Default.Apps),
+        ShortcutHelperCategory(R.string.shortcut_helper_category_a11y, Icons.Default.Accessibility),
+    )
+
+object ShortcutHelper {
+
+    object Shapes {
+        val singlePaneFirstCategory =
+            RoundedCornerShape(
+                topStart = Dimensions.SinglePaneCategoryCornerRadius,
+                topEnd = Dimensions.SinglePaneCategoryCornerRadius
+            )
+        val singlePaneLastCategory =
+            RoundedCornerShape(
+                bottomStart = Dimensions.SinglePaneCategoryCornerRadius,
+                bottomEnd = Dimensions.SinglePaneCategoryCornerRadius
+            )
+        val singlePaneCategory = RectangleShape
+    }
+
+    object Dimensions {
+        val SinglePaneCategoryCornerRadius = 28.dp
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
index ef4156d..1e8d239 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/view/ShortcutHelperActivity.kt
@@ -23,9 +23,12 @@
 import androidx.activity.BackEventCompat
 import androidx.activity.ComponentActivity
 import androidx.activity.OnBackPressedCallback
+import androidx.compose.ui.platform.ComposeView
 import androidx.core.view.updatePadding
 import androidx.lifecycle.flowWithLifecycle
 import androidx.lifecycle.lifecycleScope
+import com.android.compose.theme.PlatformTheme
+import com.android.systemui.keyboard.shortcut.ui.composable.ShortcutHelper
 import com.android.systemui.keyboard.shortcut.ui.viewmodel.ShortcutHelperViewModel
 import com.android.systemui.res.R
 import com.google.android.material.bottomsheet.BottomSheetBehavior
@@ -58,14 +61,30 @@
         super.onCreate(savedInstanceState)
         setContentView(R.layout.activity_keyboard_shortcut_helper)
         setUpBottomSheetWidth()
+        expandBottomSheet()
         setUpInsets()
         setUpPredictiveBack()
         setUpSheetDismissListener()
         setUpDismissOnTouchOutside()
+        setUpComposeView()
         observeFinishRequired()
         viewModel.onViewOpened()
     }
 
+    private fun setUpComposeView() {
+        requireViewById<ComposeView>(R.id.shortcut_helper_compose_container).apply {
+            setContent {
+                PlatformTheme {
+                    ShortcutHelper(
+                        onKeyboardSettingsClicked = ::onKeyboardSettingsClicked,
+                    )
+                }
+            }
+        }
+    }
+
+    private fun onKeyboardSettingsClicked() {}
+
     override fun onDestroy() {
         super.onDestroy()
         if (isFinishing) {
@@ -101,7 +120,8 @@
         bottomSheetContainer.setOnApplyWindowInsetsListener { _, insets ->
             val safeDrawingInsets = insets.safeDrawing
             // Make sure the bottom sheet is not covered by the status bar.
-            bottomSheetContainer.updatePadding(top = safeDrawingInsets.top)
+            bottomSheetBehavior.maxHeight =
+                resources.displayMetrics.heightPixels - safeDrawingInsets.top
             // Make sure the contents inside of the bottom sheet are not hidden by system bars, or
             // cutouts.
             bottomSheet.updatePadding(
@@ -171,7 +191,6 @@
 private val WindowInsets.safeDrawing
     get() =
         getInsets(WindowInsets.Type.systemBars())
-            .union(getInsets(WindowInsets.Type.ime()))
             .union(getInsets(WindowInsets.Type.displayCutout()))
 
 private fun Insets.union(insets: Insets): Insets = Insets.max(this, insets)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/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/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/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/BackPanelController.kt b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
index b208434..18358a7 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt
@@ -36,11 +36,12 @@
 import com.android.internal.jank.Cuj
 import com.android.internal.jank.InteractionJankMonitor
 import com.android.internal.util.LatencyTracker
-import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.NavigationEdgeBackPlugin
 import com.android.systemui.statusbar.VibratorHelper
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.ViewController
+import com.android.systemui.util.concurrency.BackPanelUiThread
+import com.android.systemui.util.concurrency.UiThreadContext
 import com.android.systemui.util.time.SystemClock
 import java.io.PrintWriter
 import javax.inject.Inject
@@ -84,11 +85,11 @@
     context: Context,
     private val windowManager: WindowManager,
     private val viewConfiguration: ViewConfiguration,
-    @Main private val mainHandler: Handler,
+    private val mainHandler: Handler,
     private val systemClock: SystemClock,
     private val vibratorHelper: VibratorHelper,
     private val configurationController: ConfigurationController,
-    private val latencyTracker: LatencyTracker,
+    latencyTracker: LatencyTracker,
     private val interactionJankMonitor: InteractionJankMonitor,
 ) : ViewController<BackPanel>(BackPanel(context, latencyTracker)), NavigationEdgeBackPlugin {
 
@@ -103,7 +104,7 @@
     constructor(
         private val windowManager: WindowManager,
         private val viewConfiguration: ViewConfiguration,
-        @Main private val mainHandler: Handler,
+        @BackPanelUiThread private val uiThreadContext: UiThreadContext,
         private val systemClock: SystemClock,
         private val vibratorHelper: VibratorHelper,
         private val configurationController: ConfigurationController,
@@ -112,20 +113,19 @@
     ) {
         /** Construct a [BackPanelController]. */
         fun create(context: Context): BackPanelController {
-            val backPanelController =
-                BackPanelController(
+            uiThreadContext.isCurrentThread()
+            return BackPanelController(
                     context,
                     windowManager,
                     viewConfiguration,
-                    mainHandler,
+                    uiThreadContext.handler,
                     systemClock,
                     vibratorHelper,
                     configurationController,
                     latencyTracker,
                     interactionJankMonitor
                 )
-            backPanelController.init()
-            return backPanelController
+                .also { it.init() }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index d0f8412..41cd2c4 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -44,8 +44,6 @@
 import android.graphics.Region;
 import android.hardware.input.InputManager;
 import android.icu.text.SimpleDateFormat;
-import android.os.Handler;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.SystemProperties;
@@ -55,7 +53,6 @@
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.TypedValue;
-import android.view.Choreographer;
 import android.view.ISystemGestureExclusionListener;
 import android.view.IWindowManager;
 import android.view.InputDevice;
@@ -75,7 +72,6 @@
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.internal.policy.GestureNavigationSettingsObserver;
 import com.android.systemui.dagger.qualifiers.Background;
-import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.plugins.FalsingManager;
@@ -94,7 +90,8 @@
 import com.android.systemui.shared.system.TaskStackChangeListener;
 import com.android.systemui.shared.system.TaskStackChangeListeners;
 import com.android.systemui.statusbar.phone.LightBarController;
-import com.android.systemui.util.Assert;
+import com.android.systemui.util.concurrency.BackPanelUiThread;
+import com.android.systemui.util.concurrency.UiThreadContext;
 import com.android.wm.shell.back.BackAnimation;
 import com.android.wm.shell.desktopmode.DesktopMode;
 import com.android.wm.shell.pip.Pip;
@@ -136,7 +133,7 @@
                 public void onSystemGestureExclusionChanged(int displayId,
                         Region systemGestureExclusion, Region unrestrictedOrNull) {
                     if (displayId == mDisplayId) {
-                        mMainExecutor.execute(() -> {
+                        mUiThreadContext.getExecutor().execute(() -> {
                             mExcludeRegion.set(systemGestureExclusion);
                             mUnrestrictedExcludeRegion.set(unrestrictedOrNull != null
                                     ? unrestrictedOrNull : systemGestureExclusion);
@@ -215,8 +212,7 @@
     private final Point mDisplaySize = new Point();
     private final int mDisplayId;
 
-    private final Executor mMainExecutor;
-    private final Handler mMainHandler;
+    private final UiThreadContext mUiThreadContext;
     private final Executor mBackgroundExecutor;
 
     private final Rect mPipExcludedBounds = new Rect();
@@ -411,8 +407,7 @@
             OverviewProxyService overviewProxyService,
             SysUiState sysUiState,
             PluginManager pluginManager,
-            @Main Executor executor,
-            @Main Handler handler,
+            @BackPanelUiThread UiThreadContext uiThreadContext,
             @Background Executor backgroundExecutor,
             UserTracker userTracker,
             NavigationModeController navigationModeController,
@@ -428,8 +423,7 @@
             Provider<LightBarController> lightBarControllerProvider) {
         mContext = context;
         mDisplayId = context.getDisplayId();
-        mMainExecutor = executor;
-        mMainHandler = handler;
+        mUiThreadContext = uiThreadContext;
         mBackgroundExecutor = backgroundExecutor;
         mUserTracker = userTracker;
         mOverviewProxyService = overviewProxyService;
@@ -478,7 +472,7 @@
                 ViewConfiguration.getLongPressTimeout());
 
         mGestureNavigationSettingsObserver = new GestureNavigationSettingsObserver(
-                mMainHandler, mContext, this::onNavigationSettingsChanged);
+                mUiThreadContext.getHandler(), mContext, this::onNavigationSettingsChanged);
 
         updateCurrentUserResources();
     }
@@ -506,11 +500,13 @@
         final boolean previousForcedVisible = mIsButtonForcedVisible;
         mIsButtonForcedVisible =
                 mGestureNavigationSettingsObserver.areNavigationButtonForcedVisible();
+        // Update this before calling mButtonForcedVisibleCallback since NavigationBar will relayout
+        // and query isHandlingGestures() as a part of the callback
+        mIsBackGestureAllowed = !mIsButtonForcedVisible;
         if (previousForcedVisible != mIsButtonForcedVisible
                 && mButtonForcedVisibleCallback != null) {
             mButtonForcedVisibleCallback.accept(mIsButtonForcedVisible);
         }
-        mIsBackGestureAllowed = !mIsButtonForcedVisible;
 
         final DisplayMetrics dm = res.getDisplayMetrics();
         final float defaultGestureHeight = res.getDimension(
@@ -564,13 +560,15 @@
         mIsAttached = true;
         mOverviewProxyService.addCallback(mQuickSwitchListener);
         mSysUiState.addCallback(mSysUiStateCallback);
-        mInputManager.registerInputDeviceListener(mInputDeviceListener, mMainHandler);
-        int [] inputDevices = mInputManager.getInputDeviceIds();
+        mInputManager.registerInputDeviceListener(
+                mInputDeviceListener,
+                mUiThreadContext.getHandler());
+        int[] inputDevices = mInputManager.getInputDeviceIds();
         for (int inputDeviceId : inputDevices) {
             mInputDeviceListener.onInputDeviceAdded(inputDeviceId);
         }
         updateIsEnabled();
-        mUserTracker.addCallback(mUserChangedCallback, mMainExecutor);
+        mUserTracker.addCallback(mUserChangedCallback, mUiThreadContext.getExecutor());
     }
 
     /**
@@ -617,6 +615,10 @@
     }
 
     private void updateIsEnabled() {
+        mUiThreadContext.runWithScissors(this::updateIsEnabledInner);
+    }
+
+    private void updateIsEnabledInner() {
         try {
             Trace.beginSection("EdgeBackGestureHandler#updateIsEnabled");
 
@@ -661,12 +663,12 @@
                 TaskStackChangeListeners.getInstance().registerTaskStackListener(
                         mTaskStackListener);
                 DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
-                        mMainExecutor::execute, mOnPropertiesChangedListener);
+                        mUiThreadContext.getExecutor()::execute, mOnPropertiesChangedListener);
                 mPipOptional.ifPresent(pip -> pip.setOnIsInPipStateChangedListener(
                         mOnIsInPipStateChangedListener));
                 mDesktopModeOptional.ifPresent(
                         dm -> dm.addDesktopGestureExclusionRegionListener(
-                                mDesktopCornersChangedListener, mMainExecutor));
+                                mDesktopCornersChangedListener, mUiThreadContext.getExecutor()));
 
                 try {
                     mWindowManagerService.registerSystemGestureExclusionListener(
@@ -677,8 +679,8 @@
 
                 // Register input event receiver
                 mInputMonitor = new InputMonitorCompat("edge-swipe", mDisplayId);
-                mInputEventReceiver = mInputMonitor.getInputReceiver(Looper.getMainLooper(),
-                        Choreographer.getInstance(), this::onInputEvent);
+                mInputEventReceiver = mInputMonitor.getInputReceiver(mUiThreadContext.getLooper(),
+                        mUiThreadContext.getChoreographer(), this::onInputEvent);
 
                 // Add a nav bar panel window
                 resetEdgeBackPlugin();
@@ -773,7 +775,7 @@
         mUseMLModel = newState;
 
         if (mUseMLModel) {
-            Assert.isMainThread();
+            mUiThreadContext.isCurrentThread();
             if (mMLModelIsLoading) {
                 Log.d(TAG, "Model tried to load while already loading.");
                 return;
@@ -804,12 +806,13 @@
         }
         BackGestureTfClassifierProvider finalProvider = provider;
         Map<String, Integer> finalVocab = vocab;
-        mMainExecutor.execute(() -> onMLModelLoadFinished(finalProvider, finalVocab, threshold));
+        mUiThreadContext.getExecutor().execute(
+                () -> onMLModelLoadFinished(finalProvider, finalVocab, threshold));
     }
 
     private void onMLModelLoadFinished(BackGestureTfClassifierProvider provider,
             Map<String, Integer> vocab, float threshold) {
-        Assert.isMainThread();
+        mUiThreadContext.isCurrentThread();
         mMLModelIsLoading = false;
         if (!mUseMLModel) {
             // This can happen if the user disables Gesture Nav while the model is loading.
@@ -1291,7 +1294,7 @@
         updateBackAnimationThresholds();
         if (mLightBarControllerProvider.get() != null) {
             mBackAnimation.setStatusBarCustomizer((appearance) -> {
-                mMainExecutor.execute(() ->
+                mUiThreadContext.getExecutor().execute(() ->
                         mLightBarControllerProvider.get()
                                 .customizeStatusBarAppearance(appearance));
             });
@@ -1308,8 +1311,7 @@
         private final OverviewProxyService mOverviewProxyService;
         private final SysUiState mSysUiState;
         private final PluginManager mPluginManager;
-        private final Executor mExecutor;
-        private final Handler mHandler;
+        private final UiThreadContext mUiThreadContext;
         private final Executor mBackgroundExecutor;
         private final UserTracker mUserTracker;
         private final NavigationModeController mNavigationModeController;
@@ -1327,29 +1329,27 @@
 
         @Inject
         public Factory(OverviewProxyService overviewProxyService,
-                       SysUiState sysUiState,
-                       PluginManager pluginManager,
-                       @Main Executor executor,
-                       @Main Handler handler,
-                       @Background Executor backgroundExecutor,
-                       UserTracker userTracker,
-                       NavigationModeController navigationModeController,
-                       BackPanelController.Factory backPanelControllerFactory,
-                       ViewConfiguration viewConfiguration,
-                       WindowManager windowManager,
-                       IWindowManager windowManagerService,
-                       InputManager inputManager,
-                       Optional<Pip> pipOptional,
-                       Optional<DesktopMode> desktopModeOptional,
-                       FalsingManager falsingManager,
-                       Provider<BackGestureTfClassifierProvider>
-                               backGestureTfClassifierProviderProvider,
-                       Provider<LightBarController> lightBarControllerProvider) {
+                        SysUiState sysUiState,
+                        PluginManager pluginManager,
+                        @BackPanelUiThread UiThreadContext uiThreadContext,
+                        @Background Executor backgroundExecutor,
+                        UserTracker userTracker,
+                        NavigationModeController navigationModeController,
+                        BackPanelController.Factory backPanelControllerFactory,
+                        ViewConfiguration viewConfiguration,
+                        WindowManager windowManager,
+                        IWindowManager windowManagerService,
+                        InputManager inputManager,
+                        Optional<Pip> pipOptional,
+                        Optional<DesktopMode> desktopModeOptional,
+                        FalsingManager falsingManager,
+                        Provider<BackGestureTfClassifierProvider>
+                                backGestureTfClassifierProviderProvider,
+                        Provider<LightBarController> lightBarControllerProvider) {
             mOverviewProxyService = overviewProxyService;
             mSysUiState = sysUiState;
             mPluginManager = pluginManager;
-            mExecutor = executor;
-            mHandler = handler;
+            mUiThreadContext = uiThreadContext;
             mBackgroundExecutor = backgroundExecutor;
             mUserTracker = userTracker;
             mNavigationModeController = navigationModeController;
@@ -1367,26 +1367,26 @@
 
         /** Construct a {@link EdgeBackGestureHandler}. */
         public EdgeBackGestureHandler create(Context context) {
-            return new EdgeBackGestureHandler(
-                    context,
-                    mOverviewProxyService,
-                    mSysUiState,
-                    mPluginManager,
-                    mExecutor,
-                    mHandler,
-                    mBackgroundExecutor,
-                    mUserTracker,
-                    mNavigationModeController,
-                    mBackPanelControllerFactory,
-                    mViewConfiguration,
-                    mWindowManager,
-                    mWindowManagerService,
-                    mInputManager,
-                    mPipOptional,
-                    mDesktopModeOptional,
-                    mFalsingManager,
-                    mBackGestureTfClassifierProviderProvider,
-                    mLightBarControllerProvider);
+            return mUiThreadContext.runWithScissors(
+                    () -> new EdgeBackGestureHandler(
+                            context,
+                            mOverviewProxyService,
+                            mSysUiState,
+                            mPluginManager,
+                            mUiThreadContext,
+                            mBackgroundExecutor,
+                            mUserTracker,
+                            mNavigationModeController,
+                            mBackPanelControllerFactory,
+                            mViewConfiguration,
+                            mWindowManager,
+                            mWindowManagerService,
+                            mInputManager,
+                            mPipOptional,
+                            mDesktopModeOptional,
+                            mFalsingManager,
+                            mBackGestureTfClassifierProviderProvider,
+                            mLightBarControllerProvider));
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/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/KeyboardShortcuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
index d00916a..c742f641 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcuts.java
@@ -610,7 +610,7 @@
             keyboardShortcutInfoAppItems.add(new KeyboardShortcutInfo(
                     mContext.getString(R.string.keyboard_shortcut_group_applications_calendar),
                     calendarIcon,
-                    KeyEvent.KEYCODE_L,
+                    KeyEvent.KEYCODE_K,
                     KeyEvent.META_META_ON));
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/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/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/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/util/concurrency/SysUIConcurrencyModule.kt b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.kt
index 83e3428..a7abb6b 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.kt
@@ -19,6 +19,7 @@
 import android.os.HandlerThread
 import android.os.Looper
 import android.os.Process
+import android.view.Choreographer
 import com.android.systemui.Dependency
 import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
@@ -31,6 +32,12 @@
 import dagger.Provides
 import java.util.concurrent.Executor
 import javax.inject.Named
+import javax.inject.Qualifier
+
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class BackPanelUiThread
 
 /** Dagger Module for classes found within the concurrent package. */
 @Module
@@ -106,6 +113,39 @@
         return looper
     }
 
+    @Provides
+    @SysUISingleton
+    @BackPanelUiThread
+    fun provideBackPanelUiThreadContext(
+        @Main mainLooper: Looper,
+        @Main mainHandler: Handler,
+        @Main mainExecutor: Executor
+    ): UiThreadContext {
+        return if (Flags.edgeBackGestureHandlerThread()) {
+            val thread =
+                HandlerThread("BackPanelUiThread", Process.THREAD_PRIORITY_DISPLAY).apply {
+                    start()
+                    looper.setSlowLogThresholdMs(
+                        LONG_SLOW_DISPATCH_THRESHOLD,
+                        LONG_SLOW_DELIVERY_THRESHOLD
+                    )
+                }
+            UiThreadContext(
+                thread.looper,
+                thread.threadHandler,
+                thread.threadExecutor,
+                thread.threadHandler.runWithScissors { Choreographer.getInstance() }
+            )
+        } else {
+            UiThreadContext(
+                mainLooper,
+                mainHandler,
+                mainExecutor,
+                mainHandler.runWithScissors { Choreographer.getInstance() }
+            )
+        }
+    }
+
     /**
      * Background Handler.
      *
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/UiThreadContext.kt b/packages/SystemUI/src/com/android/systemui/util/concurrency/UiThreadContext.kt
new file mode 100644
index 0000000..8c8c686
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/UiThreadContext.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.concurrency
+
+import android.os.Handler
+import android.os.Looper
+import android.view.Choreographer
+import com.android.systemui.util.Assert
+import java.util.concurrent.Executor
+import java.util.concurrent.atomic.AtomicReference
+
+private const val DEFAULT_TIMEOUT = 150L
+
+class UiThreadContext(
+    val looper: Looper,
+    val handler: Handler,
+    val executor: Executor,
+    val choreographer: Choreographer
+) {
+    fun isCurrentThread() {
+        Assert.isCurrentThread(looper)
+    }
+
+    fun <T> runWithScissors(block: () -> T): T {
+        return handler.runWithScissors(block)
+    }
+
+    fun runWithScissors(block: Runnable) {
+        handler.runWithScissors(block, DEFAULT_TIMEOUT)
+    }
+}
+
+fun <T> Handler.runWithScissors(block: () -> T): T {
+    val returnedValue = AtomicReference<T>()
+    runWithScissors({ returnedValue.set(block()) }, DEFAULT_TIMEOUT)
+    return returnedValue.get()!!
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
index c6b0dc5..e0f64b4 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
@@ -18,8 +18,6 @@
 
 import android.bluetooth.BluetoothAdapter
 import android.media.AudioDeviceInfo
-import android.media.AudioDeviceInfo.TYPE_WIRED_HEADPHONES
-import android.media.AudioDeviceInfo.TYPE_WIRED_HEADSET
 import com.android.settingslib.bluetooth.CachedBluetoothDevice
 import com.android.settingslib.bluetooth.LocalBluetoothManager
 import com.android.settingslib.media.BluetoothMediaDevice
@@ -81,30 +79,32 @@
     val isInAudioSharing: Flow<Boolean> = audioSharingRepository.inAudioSharing
 
     private fun AudioDeviceInfo.toAudioOutputDevice(): AudioOutputDevice {
-        if (type == TYPE_WIRED_HEADPHONES || type == TYPE_WIRED_HEADSET) {
+        if (
+            BluetoothAdapter.checkBluetoothAddress(address) &&
+                localBluetoothManager != null &&
+                bluetoothAdapter != null
+        ) {
+            val remoteDevice = bluetoothAdapter.getRemoteDevice(address)
+            localBluetoothManager.cachedDeviceManager.findDevice(remoteDevice)?.let {
+                device: CachedBluetoothDevice ->
+                return AudioOutputDevice.Bluetooth(
+                    name = device.name,
+                    icon = deviceIconInteractor.loadIcon(device),
+                    cachedBluetoothDevice = device,
+                )
+            }
+        }
+        // Built-in device has an empty address
+        if (address.isNotEmpty()) {
             return AudioOutputDevice.Wired(
                 name = productName.toString(),
                 icon = deviceIconInteractor.loadIcon(type),
             )
         }
-        val cachedBluetoothDevice: CachedBluetoothDevice? =
-            if (address.isEmpty() || localBluetoothManager == null || bluetoothAdapter == null) {
-                null
-            } else {
-                val remoteDevice = bluetoothAdapter.getRemoteDevice(address)
-                localBluetoothManager.cachedDeviceManager.findDevice(remoteDevice)
-            }
-        return cachedBluetoothDevice?.let {
-            AudioOutputDevice.Bluetooth(
-                name = it.name,
-                icon = deviceIconInteractor.loadIcon(it),
-                cachedBluetoothDevice = it,
-            )
-        }
-            ?: AudioOutputDevice.BuiltIn(
-                name = productName.toString(),
-                icon = deviceIconInteractor.loadIcon(type),
-            )
+        return AudioOutputDevice.BuiltIn(
+            name = productName.toString(),
+            icon = deviceIconInteractor.loadIcon(type),
+        )
     }
 
     private fun MediaDevice.toAudioOutputDevice(): AudioOutputDevice {
@@ -115,7 +115,8 @@
                     icon = icon,
                     cachedBluetoothDevice = cachedDevice,
                 )
-            deviceType == MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE ->
+            deviceType == MediaDeviceType.TYPE_3POINT5_MM_AUDIO_DEVICE ||
+                deviceType == MediaDeviceType.TYPE_USB_C_AUDIO_DEVICE ->
                 AudioOutputDevice.Wired(
                     name = name,
                     icon = icon,
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt b/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt
index 99f95648..3da725b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/ui/navigation/VolumeNavigator.kt
@@ -98,12 +98,12 @@
 
     private fun showNewVolumePanel() {
         activityStarter.dismissKeyguardThenExecute(
-            {
+            /* action = */ {
                 volumePanelGlobalStateInteractor.setVisible(true)
                 false
             },
-            {},
-            true
+            /* cancel = */ {},
+            /* afterKeyguardGone = */ true,
         )
     }
 
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/data/repository/TestAudioDevicesFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/TestAudioDevicesFactory.kt
index 3ac7129..15ef26d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/TestAudioDevicesFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/data/repository/TestAudioDevicesFactory.kt
@@ -33,19 +33,22 @@
         )
     }
 
-    fun wiredDevice(deviceName: String = "wired"): AudioDeviceInfo {
+    fun wiredDevice(
+        deviceName: String = "wired",
+        deviceAddress: String = "card=1;device=0",
+    ): AudioDeviceInfo {
         return AudioDeviceInfo(
             AudioDevicePort.createForTesting(
                 AudioDeviceInfo.TYPE_WIRED_HEADPHONES,
                 deviceName,
-                "",
+                deviceAddress,
             )
         )
     }
 
     fun bluetoothDevice(
         deviceName: String = "bt",
-        deviceAddress: String = "test_address",
+        deviceAddress: String = "00:43:A8:23:10:F0",
     ): AudioDeviceInfo {
         return AudioDeviceInfo(
             AudioDevicePort.createForTesting(
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/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 215f640..4a99007 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -29,6 +29,7 @@
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_RECENTS;
 import static android.content.pm.PackageManager.ACTION_REQUEST_PERMISSIONS;
 import static android.companion.virtualdevice.flags.Flags.virtualCameraServiceDiscovery;
+import static android.companion.virtualdevice.flags.Flags.intentInterceptionActionMatchingFix;
 
 import android.annotation.EnforcePermission;
 import android.annotation.NonNull;
@@ -1478,7 +1479,13 @@
         synchronized (mVirtualDeviceLock) {
             boolean hasInterceptedIntent = false;
             for (Map.Entry<IBinder, IntentFilter> interceptor : mIntentInterceptors.entrySet()) {
-                if (interceptor.getValue().match(
+                IntentFilter intentFilter = interceptor.getValue();
+                // Explicitly match the actions because the intent filter will match any intent
+                // without an explicit action. If the intent has no action, then require that there
+                // are no actions specified in the filter either.
+                boolean explicitActionMatch = !intentInterceptionActionMatchingFix()
+                        || intent.getAction() != null || intentFilter.countActions() == 0;
+                if (explicitActionMatch && intentFilter.match(
                         intent.getAction(), intent.getType(), intent.getScheme(), intent.getData(),
                         intent.getCategories(), TAG) >= 0) {
                     try {
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 966478e..a619257 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -138,12 +138,6 @@
 
     static final long DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS = TimeUnit.MINUTES.toMillis(10);
 
-    // Time needed to apply mitigation
-    private static final String MITIGATION_WINDOW_MS =
-            "persist.device_config.configuration.mitigation_window_ms";
-    @VisibleForTesting
-    static final long DEFAULT_MITIGATION_WINDOW_MS = TimeUnit.SECONDS.toMillis(5);
-
     // Threshold level at which or above user might experience significant disruption.
     private static final String MAJOR_USER_IMPACT_LEVEL_THRESHOLD =
             "persist.device_config.configuration.major_user_impact_level_threshold";
@@ -216,9 +210,6 @@
     @GuardedBy("mLock")
     private boolean mSyncRequired = false;
 
-    @GuardedBy("mLock")
-    private long mLastMitigation = -1000000;
-
     @FunctionalInterface
     @VisibleForTesting
     interface SystemClock {
@@ -409,16 +400,6 @@
             Slog.w(TAG, "Could not resolve a list of failing packages");
             return;
         }
-        synchronized (mLock) {
-            final long now = mSystemClock.uptimeMillis();
-            if (Flags.recoverabilityDetection()) {
-                if (now >= mLastMitigation
-                        && (now - mLastMitigation) < getMitigationWindowMs()) {
-                    Slog.i(TAG, "Skipping onPackageFailure mitigation");
-                    return;
-                }
-            }
-        }
         mLongTaskHandler.post(() -> {
             synchronized (mLock) {
                 if (mAllObservers.isEmpty()) {
@@ -519,17 +500,10 @@
                               int currentObserverImpact,
                               int mitigationCount) {
         if (currentObserverImpact < getUserImpactLevelLimit()) {
-            synchronized (mLock) {
-                mLastMitigation = mSystemClock.uptimeMillis();
-            }
             currentObserverToNotify.execute(versionedPackage, failureReason, mitigationCount);
         }
     }
 
-    private long getMitigationWindowMs() {
-        return SystemProperties.getLong(MITIGATION_WINDOW_MS, DEFAULT_MITIGATION_WINDOW_MS);
-    }
-
 
     /**
      * Called when the system server boots. If the system server is detected to be in a boot loop,
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/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/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 363684f..09605fe 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -59,7 +59,7 @@
     public abstract void requestCreateSession(
             long requestId,
             String packageName,
-            String routeId,
+            String routeOriginalId,
             @Nullable Bundle sessionHints,
             @RoutingSessionInfo.TransferReason int transferReason,
             @NonNull UserHandle transferInitiatorUserHandle,
@@ -77,13 +77,15 @@
             long requestId,
             @NonNull UserHandle transferInitiatorUserHandle,
             @NonNull String transferInitiatorPackageName,
-            String sessionId,
-            String routeId,
+            String sessionOriginalId,
+            String routeOriginalId,
             @RoutingSessionInfo.TransferReason int transferReason);
 
-    public abstract void setRouteVolume(long requestId, String routeId, int volume);
-    public abstract void setSessionVolume(long requestId, String sessionId, int volume);
-    public abstract void prepareReleaseSession(@NonNull String sessionId);
+    public abstract void setRouteVolume(long requestId, String routeOriginalId, int volume);
+
+    public abstract void setSessionVolume(long requestId, String sessionOriginalId, int volume);
+
+    public abstract void prepareReleaseSession(@NonNull String sessionUniqueId);
 
     @NonNull
     public String getUniqueId() {
@@ -197,8 +199,8 @@
          */
         public final long mRequestId;
 
-        /** The {@link MediaRoute2Info#getId() id} of the target route. */
-        @NonNull public final String mTargetRouteId;
+        /** The {@link MediaRoute2Info#getOriginalId()} original id} of the target route. */
+        @NonNull public final String mTargetOriginalRouteId;
 
         @RoutingSessionInfo.TransferReason public final int mTransferReason;
 
@@ -209,23 +211,23 @@
 
         SessionCreationOrTransferRequest(
                 long requestId,
-                @NonNull String routeId,
+                @NonNull String targetOriginalRouteId,
                 @RoutingSessionInfo.TransferReason int transferReason,
                 @NonNull UserHandle transferInitiatorUserHandle,
                 @NonNull String transferInitiatorPackageName) {
             mRequestId = requestId;
-            mTargetRouteId = routeId;
+            mTargetOriginalRouteId = targetOriginalRouteId;
             mTransferReason = transferReason;
             mTransferInitiatorUserHandle = transferInitiatorUserHandle;
             mTransferInitiatorPackageName = transferInitiatorPackageName;
         }
 
         public boolean isTargetRoute(@Nullable MediaRoute2Info route2Info) {
-            return route2Info != null && mTargetRouteId.equals(route2Info.getId());
+            return route2Info != null && mTargetOriginalRouteId.equals(route2Info.getOriginalId());
         }
 
-        public boolean isTargetRouteIdInList(@NonNull List<String> routesList) {
-            return routesList.stream().anyMatch(mTargetRouteId::equals);
+        public boolean isTargetRouteIdInList(@NonNull List<String> routeOriginalIdList) {
+            return routeOriginalIdList.stream().anyMatch(mTargetOriginalRouteId::equals);
         }
     }
 }
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index 386657e..71cbcb9 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -103,13 +103,14 @@
     public void requestCreateSession(
             long requestId,
             String packageName,
-            String routeId,
+            String routeOriginalId,
             Bundle sessionHints,
             @RoutingSessionInfo.TransferReason int transferReason,
             @NonNull UserHandle transferInitiatorUserHandle,
             @NonNull String transferInitiatorPackageName) {
         if (mConnectionReady) {
-            mActiveConnection.requestCreateSession(requestId, packageName, routeId, sessionHints);
+            mActiveConnection.requestCreateSession(
+                    requestId, packageName, routeOriginalId, sessionHints);
             updateBinding();
         }
     }
@@ -153,35 +154,35 @@
             long requestId,
             @NonNull UserHandle transferInitiatorUserHandle,
             @NonNull String transferInitiatorPackageName,
-            String sessionId,
-            String routeId,
+            String sessionOriginalId,
+            String routeOriginalId,
             @RoutingSessionInfo.TransferReason int transferReason) {
         if (mConnectionReady) {
-            mActiveConnection.transferToRoute(requestId, sessionId, routeId);
+            mActiveConnection.transferToRoute(requestId, sessionOriginalId, routeOriginalId);
         }
     }
 
     @Override
-    public void setRouteVolume(long requestId, String routeId, int volume) {
+    public void setRouteVolume(long requestId, String routeOriginalId, int volume) {
         if (mConnectionReady) {
-            mActiveConnection.setRouteVolume(requestId, routeId, volume);
+            mActiveConnection.setRouteVolume(requestId, routeOriginalId, volume);
             updateBinding();
         }
     }
 
     @Override
-    public void setSessionVolume(long requestId, String sessionId, int volume) {
+    public void setSessionVolume(long requestId, String sessionOriginalId, int volume) {
         if (mConnectionReady) {
-            mActiveConnection.setSessionVolume(requestId, sessionId, volume);
+            mActiveConnection.setSessionVolume(requestId, sessionOriginalId, volume);
             updateBinding();
         }
     }
 
     @Override
-    public void prepareReleaseSession(@NonNull String sessionId) {
+    public void prepareReleaseSession(@NonNull String sessionUniqueId) {
         synchronized (mLock) {
             for (RoutingSessionInfo session : mSessionInfos) {
-                if (TextUtils.equals(session.getId(), sessionId)) {
+                if (TextUtils.equals(session.getId(), sessionUniqueId)) {
                     mSessionInfos.remove(session);
                     mReleasingSessions.add(session);
                     break;
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 76930a0..6b409ee 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -158,20 +158,20 @@
     public void requestCreateSession(
             long requestId,
             String packageName,
-            String routeId,
+            String routeOriginalId,
             Bundle sessionHints,
             @RoutingSessionInfo.TransferReason int transferReason,
             @NonNull UserHandle transferInitiatorUserHandle,
             @NonNull String transferInitiatorPackageName) {
         // Assume a router without MODIFY_AUDIO_ROUTING permission can't request with
         // a route ID different from the default route ID. The service should've filtered.
-        if (TextUtils.equals(routeId, MediaRoute2Info.ROUTE_ID_DEFAULT)) {
+        if (TextUtils.equals(routeOriginalId, MediaRoute2Info.ROUTE_ID_DEFAULT)) {
             mCallback.onSessionCreated(this, requestId, mDefaultSessionInfo);
             return;
         }
 
         if (!Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
-            if (TextUtils.equals(routeId, mSelectedRouteId)) {
+            if (TextUtils.equals(routeOriginalId, mSelectedRouteId)) {
                 RoutingSessionInfo currentSessionInfo;
                 synchronized (mLock) {
                     currentSessionInfo = mSessionInfos.get(0);
@@ -192,7 +192,7 @@
             mPendingSessionCreationOrTransferRequest =
                     new SessionCreationOrTransferRequest(
                             requestId,
-                            routeId,
+                            routeOriginalId,
                             RoutingSessionInfo.TRANSFER_REASON_FALLBACK,
                             transferInitiatorUserHandle,
                             transferInitiatorPackageName);
@@ -204,7 +204,7 @@
                 transferInitiatorUserHandle,
                 transferInitiatorPackageName,
                 SYSTEM_SESSION_ID,
-                routeId,
+                routeOriginalId,
                 transferReason);
     }
 
@@ -234,15 +234,15 @@
             long requestId,
             @NonNull UserHandle transferInitiatorUserHandle,
             @NonNull String transferInitiatorPackageName,
-            String sessionId,
-            String routeId,
+            String sessionOriginalId,
+            String routeOriginalId,
             @RoutingSessionInfo.TransferReason int transferReason) {
         String selectedDeviceRouteId = mDeviceRouteController.getSelectedRoute().getId();
-        if (TextUtils.equals(routeId, MediaRoute2Info.ROUTE_ID_DEFAULT)) {
+        if (TextUtils.equals(routeOriginalId, MediaRoute2Info.ROUTE_ID_DEFAULT)) {
             if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) {
                 // Transfer to the default route (which is the selected route). We replace the id to
                 // be the selected route id so that the transfer reason gets updated.
-                routeId = selectedDeviceRouteId;
+                routeOriginalId = selectedDeviceRouteId;
             } else {
                 Log.w(TAG, "Ignoring transfer to " + MediaRoute2Info.ROUTE_ID_DEFAULT);
                 return;
@@ -254,18 +254,18 @@
                 mPendingTransferRequest =
                         new SessionCreationOrTransferRequest(
                                 requestId,
-                                routeId,
+                                routeOriginalId,
                                 transferReason,
                                 transferInitiatorUserHandle,
                                 transferInitiatorPackageName);
             }
         }
 
-        String finalRouteId = routeId; // Make a final copy to use it in the lambda.
+        String finalRouteId = routeOriginalId; // Make a final copy to use it in the lambda.
         boolean isAvailableDeviceRoute =
                 mDeviceRouteController.getAvailableRoutes().stream()
                         .anyMatch(it -> it.getId().equals(finalRouteId));
-        boolean isSelectedDeviceRoute = TextUtils.equals(routeId, selectedDeviceRouteId);
+        boolean isSelectedDeviceRoute = TextUtils.equals(routeOriginalId, selectedDeviceRouteId);
 
         if (isSelectedDeviceRoute || isAvailableDeviceRoute) {
             // The requested route is managed by the device route controller. Note that the selected
@@ -273,12 +273,12 @@
             // of the routing session). If the selected device route is transferred to, we need to
             // make the bluetooth routes inactive so that the device route becomes the selected
             // route of the routing session.
-            mDeviceRouteController.transferTo(routeId);
+            mDeviceRouteController.transferTo(routeOriginalId);
             mBluetoothRouteController.transferTo(null);
         } else {
             // The requested route is managed by the bluetooth route controller.
             mDeviceRouteController.transferTo(null);
-            mBluetoothRouteController.transferTo(routeId);
+            mBluetoothRouteController.transferTo(routeOriginalId);
         }
 
         if (Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()
@@ -288,20 +288,20 @@
     }
 
     @Override
-    public void setRouteVolume(long requestId, String routeId, int volume) {
-        if (!TextUtils.equals(routeId, mSelectedRouteId)) {
+    public void setRouteVolume(long requestId, String routeOriginalId, int volume) {
+        if (!TextUtils.equals(routeOriginalId, mSelectedRouteId)) {
             return;
         }
         mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
     }
 
     @Override
-    public void setSessionVolume(long requestId, String sessionId, int volume) {
+    public void setSessionVolume(long requestId, String sessionOriginalId, int volume) {
         // Do nothing since we don't support grouping volume yet.
     }
 
     @Override
-    public void prepareReleaseSession(String sessionId) {
+    public void prepareReleaseSession(String sessionUniqueId) {
         // Do nothing since the system session persists.
     }
 
@@ -503,12 +503,13 @@
         }
 
         long pendingRequestId = mPendingSessionCreationOrTransferRequest.mRequestId;
-        if (mPendingSessionCreationOrTransferRequest.mTargetRouteId.equals(mSelectedRouteId)) {
+        if (mPendingSessionCreationOrTransferRequest.mTargetOriginalRouteId.equals(
+                mSelectedRouteId)) {
             if (DEBUG) {
                 Slog.w(
                         TAG,
                         "Session creation success to route "
-                                + mPendingSessionCreationOrTransferRequest.mTargetRouteId);
+                                + mPendingSessionCreationOrTransferRequest.mTargetOriginalRouteId);
             }
             mPendingSessionCreationOrTransferRequest = null;
             mCallback.onSessionCreated(this, pendingRequestId, newSessionInfo);
@@ -520,7 +521,8 @@
                     Slog.w(
                             TAG,
                             "Session creation failed to route "
-                                    + mPendingSessionCreationOrTransferRequest.mTargetRouteId);
+                                    + mPendingSessionCreationOrTransferRequest
+                                            .mTargetOriginalRouteId);
                 }
                 mPendingSessionCreationOrTransferRequest = null;
                 mCallback.onRequestFailed(
@@ -529,7 +531,7 @@
                 Slog.w(
                         TAG,
                         "Session creation waiting state to route "
-                                + mPendingSessionCreationOrTransferRequest.mTargetRouteId);
+                                + mPendingSessionCreationOrTransferRequest.mTargetOriginalRouteId);
             }
         }
     }
@@ -541,7 +543,8 @@
         // See b/307723189 for context
         for (MediaRoute2Info btRoute : mBluetoothRouteController.getAllBluetoothRoutes()) {
             if (TextUtils.equals(
-                    btRoute.getId(), mPendingSessionCreationOrTransferRequest.mTargetRouteId)) {
+                    btRoute.getId(),
+                    mPendingSessionCreationOrTransferRequest.mTargetOriginalRouteId)) {
                 return true;
             }
         }
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/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 7aa5791..2d2a88a86 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3964,7 +3964,7 @@
         }
 
         if (isCurrentVisible) {
-            if (isNextNotYetVisible || delayRemoval) {
+            if (isNextNotYetVisible || delayRemoval || (next != null && isInTransition())) {
                 // Add this activity to the list of stopping activities. It will be processed and
                 // destroyed when the next activity reports idle.
                 addToStopping(false /* scheduleIdle */, false /* idleDelayed */,
@@ -9261,6 +9261,19 @@
             @NonNull CompatDisplayInsets compatDisplayInsets) {
         final Configuration resolvedConfig = getResolvedOverrideConfiguration();
         final Rect resolvedBounds = resolvedConfig.windowConfiguration.getBounds();
+        final Insets insets;
+        if (mResolveConfigHint.mUseOverrideInsetsForConfig) {
+            // TODO(b/343197837): Add test to verify SCM behaviour with new bound configuration
+            // Insets are decoupled from configuration by default from V+, use legacy
+            // compatibility behaviour for apps targeting SDK earlier than 35
+            // (see applySizeOverrideIfNeeded).
+            insets = Insets.of(mDisplayContent.getDisplayPolicy()
+                    .getDecorInsetsInfo(mDisplayContent.mDisplayFrames.mRotation,
+                            mDisplayContent.mDisplayFrames.mWidth,
+                            mDisplayContent.mDisplayFrames.mHeight).mOverrideNonDecorInsets);
+        } else {
+            insets = Insets.NONE;
+        }
 
         // When an activity needs to be letterboxed because of fixed orientation, use fixed
         // orientation bounds (stored in resolved bounds) instead of parent bounds since the
@@ -9271,9 +9284,12 @@
         final Rect containerBounds = useResolvedBounds
                 ? new Rect(resolvedBounds)
                 : newParentConfiguration.windowConfiguration.getBounds();
+        final Rect parentAppBounds =
+                newParentConfiguration.windowConfiguration.getAppBounds();
+        parentAppBounds.inset(insets);
         final Rect containerAppBounds = useResolvedBounds
                 ? new Rect(resolvedConfig.windowConfiguration.getAppBounds())
-                : newParentConfiguration.windowConfiguration.getAppBounds();
+                : parentAppBounds;
 
         final int requestedOrientation = getRequestedConfigurationOrientation();
         final boolean orientationRequested = requestedOrientation != ORIENTATION_UNDEFINED;
diff --git a/services/core/java/com/android/server/wm/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/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 94a2239..a00b6fc4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1927,7 +1927,7 @@
             displayContent.getInsetsStateController().updateAboveInsetsState(
                     false /* notifyInsetsChanged */);
 
-            outInsetsState.set(win.getCompatInsetsState(), true /* copySources */);
+            win.fillInsetsState(outInsetsState, true /* copySources */);
             getInsetsSourceControls(win, outActiveControls);
 
             if (win.mLayoutAttached) {
@@ -2680,7 +2680,7 @@
             }
 
             if (outInsetsState != null) {
-                outInsetsState.set(win.getCompatInsetsState(), true /* copySources */);
+                win.fillInsetsState(outInsetsState, true /* copySources */);
             }
 
             ProtoLog.v(WM_DEBUG_FOCUS, "Relayout of %s: focusMayChange=%b",
@@ -2743,12 +2743,10 @@
     }
 
     private void getInsetsSourceControls(WindowState win, InsetsSourceControl.Array outArray) {
-        final InsetsSourceControl[] controls =
-                win.getDisplayContent().getInsetsStateController().getControlsForDispatch(win);
         // We will leave the critical section before returning the leash to the client,
         // so we need to copy the leash to prevent others release the one that we are
         // about to return.
-        outArray.set(controls, true /* copyControls */);
+        win.fillInsetsSourceControls(outArray, true /* copyControls */);
         // This source control is an extra copy if the client is not local. By setting
         // PARCELABLE_WRITE_RETURN_VALUE, the leash will be released at the end of
         // SurfaceControl.writeToParcel.
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index d7c49ac..d1efc27 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -434,7 +434,10 @@
     /** @see #isLastConfigReportedToClient() */
     private boolean mLastConfigReportedToClient;
 
-    // TODO(b/339380439): Ensure to use the same object for IWindowSession#relayout
+    private final ClientWindowFrames mLastReportedFrames = new ClientWindowFrames();
+
+    private final InsetsState mLastReportedInsetsState = new InsetsState();
+
     private final InsetsSourceControl.Array mLastReportedActiveControls =
             new InsetsSourceControl.Array();
 
@@ -495,8 +498,6 @@
 
     private final WindowFrames mWindowFrames = new WindowFrames();
 
-    private final ClientWindowFrames mClientWindowFrames = new ClientWindowFrames();
-
     /**
      * List of rects where system gestures should be ignored.
      *
@@ -3650,8 +3651,10 @@
                 outFrames.attachedFrame.scale(mInvGlobalScale);
             }
         }
-
         outFrames.compatScale = getCompatScaleForClient();
+        if (mLastReportedFrames != outFrames) {
+            mLastReportedFrames.setTo(outFrames);
+        }
 
         // Note: in the cases where the window is tied to an activity, we should not send a
         // configuration update when the window has requested to be hidden. Doing so can lead to
@@ -3678,6 +3681,25 @@
         mLastConfigReportedToClient = true;
     }
 
+    void fillInsetsState(@NonNull InsetsState outInsetsState, boolean copySources) {
+        outInsetsState.set(getCompatInsetsState(), copySources);
+        if (outInsetsState != mLastReportedInsetsState) {
+            // No need to copy for the recorded.
+            mLastReportedInsetsState.set(outInsetsState, false /* copySources */);
+        }
+    }
+
+    void fillInsetsSourceControls(@NonNull InsetsSourceControl.Array outArray,
+            boolean copyControls) {
+        final InsetsSourceControl[] controls =
+                getDisplayContent().getInsetsStateController().getControlsForDispatch(this);
+        outArray.set(controls, copyControls);
+        if (outArray != mLastReportedActiveControls) {
+            // No need to copy for the recorded.
+            mLastReportedActiveControls.setTo(outArray, false /* copyControls */);
+        }
+    }
+
     void reportResized() {
         // If the activity is scheduled to relaunch, skip sending the resized to ViewRootImpl now
         // since it will be destroyed anyway. This also prevents the client from receiving
@@ -3712,9 +3734,10 @@
 
         final int prevRotation = mLastReportedConfiguration
                 .getMergedConfiguration().windowConfiguration.getRotation();
-        fillClientWindowFramesAndConfiguration(mClientWindowFrames, mLastReportedConfiguration,
+        fillClientWindowFramesAndConfiguration(mLastReportedFrames, mLastReportedConfiguration,
                 mLastReportedActivityWindowInfo, true /* useLatestConfig */,
                 false /* relayoutVisible */);
+        fillInsetsState(mLastReportedInsetsState, false /* copySources */);
         final boolean syncRedraw = shouldSendRedrawForSync();
         final boolean syncWithBuffers = syncRedraw && shouldSyncWithBuffers();
         final boolean reportDraw = syncRedraw || drawPending;
@@ -3734,8 +3757,8 @@
 
         if (Flags.bundleClientTransactionFlag()) {
             getProcess().scheduleClientTransactionItem(
-                    WindowStateResizeItem.obtain(mClient, mClientWindowFrames, reportDraw,
-                            mLastReportedConfiguration, getCompatInsetsState(), forceRelayout,
+                    WindowStateResizeItem.obtain(mClient, mLastReportedFrames, reportDraw,
+                            mLastReportedConfiguration, mLastReportedInsetsState, forceRelayout,
                             alwaysConsumeSystemBars, displayId,
                             syncWithBuffers ? mSyncSeqId : -1, isDragResizing,
                             mLastReportedActivityWindowInfo));
@@ -3743,8 +3766,8 @@
         } else {
             // TODO(b/301870955): cleanup after launch
             try {
-                mClient.resized(mClientWindowFrames, reportDraw, mLastReportedConfiguration,
-                        getCompatInsetsState(), forceRelayout, alwaysConsumeSystemBars, displayId,
+                mClient.resized(mLastReportedFrames, reportDraw, mLastReportedConfiguration,
+                        mLastReportedInsetsState, forceRelayout, alwaysConsumeSystemBars, displayId,
                         syncWithBuffers ? mSyncSeqId : -1, isDragResizing,
                         mLastReportedActivityWindowInfo);
                 onResizePostDispatched(drawPending, prevRotation, displayId);
@@ -3817,17 +3840,14 @@
         if (mRemoved) {
             return;
         }
-        final InsetsStateController stateController =
-                getDisplayContent().getInsetsStateController();
-        final InsetsState insetsState = getCompatInsetsState();
-        mLastReportedActiveControls.set(stateController.getControlsForDispatch(this),
-                false /* copyControls */);
+        fillInsetsState(mLastReportedInsetsState, false /* copySources */);
+        fillInsetsSourceControls(mLastReportedActiveControls, false /* copyControls */);
         if (Flags.insetsControlChangedItem()) {
             getProcess().scheduleClientTransactionItem(WindowStateInsetsControlChangeItem.obtain(
-                    mClient, insetsState, mLastReportedActiveControls));
+                    mClient, mLastReportedInsetsState, mLastReportedActiveControls));
         } else {
             try {
-                mClient.insetsControlChanged(insetsState, mLastReportedActiveControls);
+                mClient.insetsControlChanged(mLastReportedInsetsState, mLastReportedActiveControls);
             } catch (RemoteException e) {
                 Slog.w(TAG, "Failed to deliver inset control state change to w=" + this, e);
             }
diff --git a/services/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/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
     }
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index a8b383c..093923f 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -101,8 +101,8 @@
     private static final String OBSERVER_NAME_2 = "observer2";
     private static final String OBSERVER_NAME_3 = "observer3";
     private static final String OBSERVER_NAME_4 = "observer4";
-    private static final long SHORT_DURATION = TimeUnit.SECONDS.toMillis(10);
-    private static final long LONG_DURATION = TimeUnit.SECONDS.toMillis(50);
+    private static final long SHORT_DURATION = TimeUnit.SECONDS.toMillis(1);
+    private static final long LONG_DURATION = TimeUnit.SECONDS.toMillis(5);
 
     @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@@ -1453,8 +1453,7 @@
         raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
                 VERSION_CODE)), PackageWatchdog.FAILURE_REASON_UNKNOWN);
 
-        moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_DEESCALATION_WINDOW_MS
-                - TimeUnit.MINUTES.toMillis(1));
+        moveTimeForwardAndDispatch(PackageWatchdog.DEFAULT_DEESCALATION_WINDOW_MS);
 
         // The first failure will be outside the threshold.
         raiseFatalFailureAndDispatch(watchdog, Arrays.asList(new VersionedPackage(APP_A,
@@ -1713,9 +1712,6 @@
             watchdog.onPackageFailure(packages, failureReason);
         }
         mTestLooper.dispatchAll();
-        if (Flags.recoverabilityDetection()) {
-            moveTimeForwardAndDispatch(watchdog.DEFAULT_MITIGATION_WINDOW_MS);
-        }
     }
 
     private PackageWatchdog createWatchdog() {