Merge "bt: Add late bonding confirmation mechanism" into udc-dev
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 3249b41..4e2b6fa 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -261,6 +261,9 @@
     void cancelTaskWindowTransition(int taskId);
 
     /**
+     * Fetches the snapshot for the task with the given id, taking a new snapshot if it is not in
+     * the task snapshot cache and it is requested.
+     *
      * @param taskId the id of the task to retrieve the sAutoapshots for
      * @param isLowResolution if set, if the snapshot needs to be loaded from disk, this will load
      *                          a reduced resolution of it, which is much faster
@@ -272,10 +275,14 @@
             int taskId, boolean isLowResolution, boolean takeSnapshotIfNeeded);
 
     /**
+     * Requests for a new snapshot to be taken for the task with the given id, storing it in the
+     * task snapshot cache only if requested.
+     *
      * @param taskId the id of the task to take a snapshot of
+     * @param updateCache whether to store the new snapshot in the system's task snapshot cache
      * @return a graphic buffer representing a screenshot of a task
      */
-    android.window.TaskSnapshot takeTaskSnapshot(int taskId);
+    android.window.TaskSnapshot takeTaskSnapshot(int taskId, boolean updateCache);
 
     /**
      * Return the user id of last resumed activity.
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index d802b46..47a5db8 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -791,4 +791,6 @@
     void setKeepUninstalledPackages(in List<String> packageList);
 
     boolean[] canPackageQuery(String sourcePackageName, in String[] targetPackageNames, int userId);
+
+    boolean waitForHandler(long timeoutMillis, boolean forBackgroundHandler);
 }
diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java
index ba54686..5ad74c8 100644
--- a/core/java/android/view/autofill/AutofillFeatureFlags.java
+++ b/core/java/android/view/autofill/AutofillFeatureFlags.java
@@ -280,7 +280,7 @@
     private static final boolean DEFAULT_AFAA_ON_IMPORTANT_VIEW_ENABLED = true;
     private static final String DEFAULT_AFAA_DENYLIST = "";
     private static final String DEFAULT_AFAA_ALLOWLIST = "";
-    private static final String DEFAULT_AFAA_NON_AUTOFILLABLE_IME_ACTIONS = "2,3,4";
+    private static final String DEFAULT_AFAA_NON_AUTOFILLABLE_IME_ACTIONS = "3,4";
     private static final boolean DEFAULT_AFAA_SHOULD_ENABLE_AUTOFILL_ON_ALL_VIEW_TYPES = true;
     private static final boolean DEFAULT_AFAA_SHOULD_ENABLE_MULTILINE_FILTER = true;
 
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index a554d0e..92cfa67 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -50,7 +50,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
-import android.provider.DeviceConfig;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
@@ -723,15 +722,13 @@
     }
 
     /**
-     * Whether the auto pin feature logic is available or not.
-     * @return true, if deviceConfig flag is set to true or the flag is not propagated and
-     * defaultValue is true.
+     * Whether the auto pin feature is available or not.
+     * @return true. This method is always returning true due to feature flags not working
+     * properly (b/282246482). Ideally, this should check if deviceConfig flag is set to true
+     * and then return the appropriate value.
      */
     public static boolean isAutoPinConfirmFeatureAvailable() {
-        return DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_AUTO_PIN_CONFIRMATION,
-                FLAG_ENABLE_AUTO_PIN_CONFIRMATION,
-                /* defaultValue= */ true);
+        return true;
     }
 
     /** Returns if the given quality maps to an alphabetic password */
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index a99ba15..5b0dd30 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -72,9 +72,9 @@
     <item name="secondary_content_alpha_material_dark" format="float" type="dimen">.7</item>
     <item name="secondary_content_alpha_material_light" format="float" type="dimen">0.60</item>
 
-    <item name="highlight_alpha_material_light" format="float" type="dimen">0.5</item>
-    <item name="highlight_alpha_material_dark" format="float" type="dimen">0.5</item>
-    <item name="highlight_alpha_material_colored" format="float" type="dimen">0.10</item>
+    <item name="highlight_alpha_material_light" format="float" type="dimen">0.20</item>
+    <item name="highlight_alpha_material_dark" format="float" type="dimen">0.20</item>
+    <item name="highlight_alpha_material_colored" format="float" type="dimen">0.20</item>
 
     <!-- Primary & accent colors -->
     <eat-comment />
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 4d0a058..641a2ae 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -1013,7 +1013,8 @@
         }
         p.setShader(shader);
         p.setColorFilter(null);
-        p.setColor(color);
+        // Alpha is handled by the shader (and color is a no-op because there's a shader)
+        p.setColor(0xFF000000);
         return properties;
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 9a2ec15..ab8e7e6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -709,7 +709,8 @@
         return context.getSystemService(WindowManager.class)
                 .getMaximumWindowMetrics()
                 .getWindowInsets()
-                .getInsets(WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout())
+                .getInsetsIgnoringVisibility(WindowInsets.Type.systemBars()
+                        | WindowInsets.Type.displayCutout())
                 .toRect();
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 74ef57e..5a9c25f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -732,10 +732,11 @@
 
     @WMSingleton
     @Provides
-    static ShellController provideShellController(ShellInit shellInit,
+    static ShellController provideShellController(Context context,
+            ShellInit shellInit,
             ShellCommandHandler shellCommandHandler,
             @ShellMainThread ShellExecutor mainExecutor) {
-        return new ShellController(shellInit, shellCommandHandler, mainExecutor);
+        return new ShellController(context, shellInit, shellCommandHandler, mainExecutor);
     }
 
     //
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
index 78de5f3..3906599 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
@@ -57,27 +57,35 @@
             in Rect destinationBounds, in SurfaceControl overlay) = 2;
 
     /**
+     * Notifies the swiping Activity to PiP onto home transition is aborted
+     *
+     * @param taskId the Task id that the Activity and overlay are currently in.
+     * @param componentName ComponentName represents the Activity
+     */
+    oneway void abortSwipePipToHome(int taskId, in ComponentName componentName) = 3;
+
+    /**
      * Sets listener to get pinned stack animation callbacks.
      */
-    oneway void setPipAnimationListener(IPipAnimationListener listener) = 3;
+    oneway void setPipAnimationListener(IPipAnimationListener listener) = 4;
 
     /**
      * Sets the shelf height and visibility.
      */
-    oneway void setShelfHeight(boolean visible, int shelfHeight) = 4;
+    oneway void setShelfHeight(boolean visible, int shelfHeight) = 5;
 
     /**
      * Sets the next pip animation type to be the alpha animation.
      */
-    oneway void setPipAnimationTypeToAlpha() = 5;
+    oneway void setPipAnimationTypeToAlpha() = 6;
 
     /**
      * Sets the height and visibility of the Launcher keep clear area.
      */
-    oneway void setLauncherKeepClearAreaHeight(boolean visible, int height) = 6;
+    oneway void setLauncherKeepClearAreaHeight(boolean visible, int height) = 7;
 
     /**
      * Sets the app icon size in pixel used by Launcher
      */
-     oneway void setLauncherAppIconSize(int iconSizePx) = 7;
+    oneway void setLauncherAppIconSize(int iconSizePx) = 8;
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 58bc81d..db7c3fc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -462,13 +462,29 @@
             // to the actual Task surface now.
             // PipTransition is responsible to fade it out and cleanup when finishing the enter PIP
             // transition.
-            final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+            final SurfaceControl.Transaction t = mSurfaceControlTransactionFactory.getTransaction();
             mTaskOrganizer.reparentChildSurfaceToTask(taskId, overlay, t);
             t.setLayer(overlay, Integer.MAX_VALUE);
             t.apply();
         }
     }
 
+    /**
+     * Callback when launcher aborts swipe-pip-to-home operation.
+     */
+    public void abortSwipePipToHome(int taskId, ComponentName componentName) {
+        if (!mPipTransitionState.getInSwipePipToHomeTransition()) {
+            return;
+        }
+        ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                "Abort swipe-pip-to-home for %s", componentName);
+        sendOnPipTransitionCancelled(TRANSITION_DIRECTION_TO_PIP);
+        // Cleanup internal states
+        mPipTransitionState.setInSwipePipToHomeTransition(false);
+        mPictureInPictureParams = null;
+        mPipTransitionState.setTransitionState(PipTransitionState.UNDEFINED);
+    }
+
     public ActivityManager.RunningTaskInfo getTaskInfo() {
         return mTaskInfo;
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 9e6bd47..63181da 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -1017,6 +1017,10 @@
         mPipTaskOrganizer.stopSwipePipToHome(taskId, componentName, destinationBounds, overlay);
     }
 
+    private void abortSwipePipToHome(int taskId, ComponentName componentName) {
+        mPipTaskOrganizer.abortSwipePipToHome(taskId, componentName);
+    }
+
     private String getTransitionTag(int direction) {
         switch (direction) {
             case TRANSITION_DIRECTION_TO_PIP:
@@ -1313,6 +1317,12 @@
         }
 
         @Override
+        public void abortSwipePipToHome(int taskId, ComponentName componentName) {
+            executeRemoteCallWithTaskPermission(mController, "abortSwipePipToHome",
+                    (controller) -> controller.abortSwipePipToHome(taskId, componentName));
+        }
+
+        @Override
         public void setShelfHeight(boolean visible, int height) {
             executeRemoteCallWithTaskPermission(mController, "setShelfHeight",
                     (controller) -> controller.setShelfHeight(visible, height));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 387d390..a9ad3c9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -301,7 +301,7 @@
                 try {
                     for (int i = 0; i < mPausingTasks.size(); ++i) {
                         snapshots[i] = ActivityTaskManager.getService().takeTaskSnapshot(
-                                mPausingTasks.get(0).mTaskInfo.taskId);
+                                mPausingTasks.get(0).mTaskInfo.taskId, false /* updateCache */);
                     }
                 } catch (RemoteException e) {
                     taskIds = null;
@@ -671,7 +671,8 @@
             try {
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
                         "[%d] RecentsController.screenshotTask: taskId=%d", mInstanceId, taskId);
-                return ActivityTaskManager.getService().takeTaskSnapshot(taskId);
+                return ActivityTaskManager.getService().takeTaskSnapshot(taskId,
+                        true /* updateCache */);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Failed to screenshot task", e);
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
index 3f944cb..0eb7c2d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellController.java
@@ -32,6 +32,7 @@
 import android.content.res.Configuration;
 import android.os.Bundle;
 import android.util.ArrayMap;
+import android.view.SurfaceControlRegistry;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.VisibleForTesting;
@@ -52,6 +53,7 @@
 public class ShellController {
     private static final String TAG = ShellController.class.getSimpleName();
 
+    private final Context mContext;
     private final ShellInit mShellInit;
     private final ShellCommandHandler mShellCommandHandler;
     private final ShellExecutor mMainExecutor;
@@ -72,8 +74,11 @@
     private Configuration mLastConfiguration;
 
 
-    public ShellController(ShellInit shellInit, ShellCommandHandler shellCommandHandler,
+    public ShellController(Context context,
+            ShellInit shellInit,
+            ShellCommandHandler shellCommandHandler,
             ShellExecutor mainExecutor) {
+        mContext = context;
         mShellInit = shellInit;
         mShellCommandHandler = shellCommandHandler;
         mMainExecutor = mainExecutor;
@@ -254,6 +259,16 @@
         }
     }
 
+    private void handleInit() {
+        SurfaceControlRegistry.createProcessInstance(mContext);
+        mShellInit.init();
+    }
+
+    private void handleDump(PrintWriter pw) {
+        mShellCommandHandler.dump(pw);
+        SurfaceControlRegistry.dump(100 /* limit */, false /* runGc */, pw);
+    }
+
     public void dump(@NonNull PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
@@ -279,7 +294,7 @@
         @Override
         public void onInit() {
             try {
-                mMainExecutor.executeBlocking(() -> mShellInit.init());
+                mMainExecutor.executeBlocking(() -> ShellController.this.handleInit());
             } catch (InterruptedException e) {
                 throw new RuntimeException("Failed to initialize the Shell in 2s", e);
             }
@@ -344,7 +359,7 @@
         @Override
         public void dump(PrintWriter pw) {
             try {
-                mMainExecutor.executeBlocking(() -> mShellCommandHandler.dump(pw));
+                mMainExecutor.executeBlocking(() -> ShellController.this.handleDump(pw));
             } catch (InterruptedException e) {
                 throw new RuntimeException("Failed to dump the Shell in 2s", e);
             }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
index 5d7b629..bb0eba6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/UnfoldTransitionHandler.java
@@ -18,6 +18,8 @@
 
 import static android.view.WindowManager.TRANSIT_CHANGE;
 
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
+
 import android.os.IBinder;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
@@ -27,6 +29,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.internal.protolog.common.ProtoLog;
 import com.android.wm.shell.common.TransactionPool;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
@@ -36,6 +39,7 @@
 import com.android.wm.shell.unfold.animation.FullscreenUnfoldTaskAnimator;
 import com.android.wm.shell.unfold.animation.SplitTaskUnfoldAnimator;
 import com.android.wm.shell.unfold.animation.UnfoldTaskAnimator;
+import com.android.wm.shell.util.TransitionUtil;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -105,8 +109,14 @@
             animator.clearTasks();
 
             info.getChanges().forEach(change -> {
-                if (change.getTaskInfo() != null
-                        && change.getMode() == TRANSIT_CHANGE
+                if (change.getTaskInfo() != null) {
+                    ProtoLog.v(WM_SHELL_TRANSITIONS,
+                            "startAnimation, check taskInfo: %s, mode: %s, isApplicableTask: %s",
+                            change.getTaskInfo(), TransitionInfo.modeToString(change.getMode()),
+                            animator.isApplicableTask(change.getTaskInfo()));
+                }
+                if (change.getTaskInfo() != null && (change.getMode() == TRANSIT_CHANGE
+                        || TransitionUtil.isOpeningType(change.getMode()))
                         && animator.isApplicableTask(change.getTaskInfo())) {
                     animator.onTaskAppeared(change.getTaskInfo(), change.getLeash());
                 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java
index 123bf3b..a4cf149 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/unfold/animation/SplitTaskUnfoldAnimator.java
@@ -213,7 +213,7 @@
     @Override
     public boolean isApplicableTask(TaskInfo taskInfo) {
         return taskInfo.hasParentTask()
-                && taskInfo.isVisible
+                && taskInfo.isRunning
                 && taskInfo.realActivity != null // to filter out parents created by organizer
                 && taskInfo.getWindowingMode() == WINDOWING_MODE_MULTI_WINDOW;
     }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 04f2c99..85167cb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -129,7 +129,7 @@
             return null;
         }).when(mMockExecutor).execute(any());
         mShellInit = spy(new ShellInit(mMockExecutor));
-        mShellController = spy(new ShellController(mShellInit, mMockShellCommandHandler,
+        mShellController = spy(new ShellController(mContext, mShellInit, mMockShellCommandHandler,
                 mMockExecutor));
         mPipController = new PipController(mContext, mShellInit, mMockShellCommandHandler,
                 mShellController, mMockDisplayController, mMockPipAnimationController,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index b542fae..2c69522 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -107,7 +107,7 @@
         mMainExecutor = new TestShellExecutor();
         when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class));
         mShellInit = spy(new ShellInit(mMainExecutor));
-        mShellController = spy(new ShellController(mShellInit, mShellCommandHandler,
+        mShellController = spy(new ShellController(mContext, mShellInit, mShellCommandHandler,
                 mMainExecutor));
         mRecentTasksControllerReal = new RecentTasksController(mContext, mShellInit,
                 mShellController, mShellCommandHandler, mTaskStackListener, mActivityTaskManager,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index c37a497..9189d3d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -111,7 +111,7 @@
     public void setup() {
         assumeTrue(ActivityTaskManager.supportsSplitScreenMultiWindow(mContext));
         MockitoAnnotations.initMocks(this);
-        mShellController = spy(new ShellController(mShellInit, mShellCommandHandler,
+        mShellController = spy(new ShellController(mContext, mShellInit, mShellCommandHandler,
                 mMainExecutor));
         mSplitScreenController = spy(new SplitScreenController(mContext, mShellInit,
                 mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java
index 10dec9e..a9082a6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java
@@ -81,7 +81,7 @@
         doReturn(mock(Display.class)).when(mDisplayManager).getDisplay(anyInt());
         doReturn(mDisplayManager).when(mContext).getSystemService(eq(DisplayManager.class));
         mShellInit = spy(new ShellInit(mMainExecutor));
-        mShellController = spy(new ShellController(mShellInit, mShellCommandHandler,
+        mShellController = spy(new ShellController(mContext, mShellInit, mShellCommandHandler,
                 mMainExecutor));
         mController = new StartingWindowController(mContext, mShellInit, mShellController,
                 mTaskOrganizer, mMainExecutor, mTypeAlgorithm, mIconProvider, mTransactionPool);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
index 8d92d08..7c520c3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/sysui/ShellControllerTest.java
@@ -78,7 +78,7 @@
         mConfigChangeListener = new TestConfigurationChangeListener();
         mUserChangeListener = new TestUserChangeListener();
         mExecutor = new TestShellExecutor();
-        mController = new ShellController(mShellInit, mShellCommandHandler, mExecutor);
+        mController = new ShellController(mContext, mShellInit, mShellCommandHandler, mExecutor);
         mController.onConfigurationChanged(getConfigurationCopy());
     }
 
diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp
index 4194a22..45dde74 100644
--- a/media/jni/soundpool/Stream.cpp
+++ b/media/jni/soundpool/Stream.cpp
@@ -282,8 +282,6 @@
             priority, loop, rate, playerIId);
 
     // initialize track
-    const audio_stream_type_t streamType =
-            AudioSystem::attributesToStreamType(*mStreamManager->getAttributes());
     const int32_t channelCount = sound->getChannelCount();
     const auto sampleRate = (uint32_t)lround(double(sound->getSampleRate()) * rate);
     size_t frameCount = 0;
@@ -328,8 +326,8 @@
         attributionSource.token = sp<BBinder>::make();
         mCallback =  sp<StreamCallback>::make(this, toggle),
         // TODO b/182469354 make consistent with AudioRecord, add util for native source
-        mAudioTrack = new AudioTrack(streamType, sampleRate, sound->getFormat(),
-                channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_FAST,
+        mAudioTrack = new AudioTrack(AUDIO_STREAM_DEFAULT, sampleRate, sound->getFormat(),
+                channelMask, sound->getIMemory(), AUDIO_OUTPUT_FLAG_NONE,
                 mCallback,
                 0 /*default notification frames*/, AUDIO_SESSION_ALLOCATE,
                 AudioTrack::TRANSFER_DEFAULT,
diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp
index acd4bad..52060f1 100644
--- a/media/jni/soundpool/StreamManager.cpp
+++ b/media/jni/soundpool/StreamManager.cpp
@@ -109,7 +109,10 @@
         int32_t streams, size_t threads, const audio_attributes_t& attributes,
         std::string opPackageName)
     : StreamMap(streams)
-    , mAttributes(attributes)
+    , mAttributes([attributes](){
+        audio_attributes_t attr = attributes;
+        attr.flags = static_cast<audio_flags_mask_t>(attr.flags | AUDIO_FLAG_LOW_LATENCY);
+        return attr; }())
     , mOpPackageName(std::move(opPackageName))
     , mLockStreamManagerStop(streams == 1 || kForceLockStreamManagerStop)
 {
diff --git a/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml b/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml
index 6f504ef..9d53e39 100644
--- a/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml
+++ b/packages/SettingsLib/BannerMessagePreference/res/layout-v31/settingslib_banner_message.xml
@@ -50,6 +50,8 @@
         android:id="@+id/banner_title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:layout_gravity="start"
+        android:textAlignment="viewStart"
         android:paddingTop="0dp"
         android:paddingBottom="4dp"
         android:textAppearance="@style/Banner.Title.SettingsLib"/>
@@ -58,6 +60,8 @@
         android:id="@+id/banner_subtitle"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:layout_gravity="start"
+        android:textAlignment="viewStart"
         android:paddingTop="0dp"
         android:paddingBottom="4dp"
         android:textAppearance="@style/Banner.Subtitle.SettingsLib"
@@ -67,6 +71,8 @@
         android:id="@+id/banner_summary"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:layout_gravity="start"
+        android:textAlignment="viewStart"
         android:paddingTop="4dp"
         android:paddingBottom="8dp"
         android:textAppearance="@style/Banner.Summary.SettingsLib"/>
diff --git a/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml b/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
index 42700b3..470a83d 100644
--- a/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
+++ b/packages/SettingsLib/FooterPreference/res/layout-v31/preference_footer.xml
@@ -50,6 +50,8 @@
             android:id="@android:id/title"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_gravity="start"
+            android:textAlignment="viewStart"
             android:paddingTop="16dp"
             android:paddingBottom="8dp"
             android:textColor="?android:attr/textColorSecondary"
@@ -60,6 +62,8 @@
             android:text="@string/settingslib_learn_more_text"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_gravity="start"
+            android:textAlignment="viewStart"
             android:paddingBottom="8dp"
             android:clickable="true"
             android:visibility="gone"
diff --git a/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml b/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml
index a2f2510..4b5fd44 100644
--- a/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml
+++ b/packages/SettingsLib/FooterPreference/res/layout-v33/preference_footer.xml
@@ -50,6 +50,8 @@
             android:id="@android:id/title"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_gravity="start"
+            android:textAlignment="viewStart"
             android:paddingTop="16dp"
             android:paddingBottom="8dp"
             android:textColor="?android:attr/textColorSecondary"
@@ -62,6 +64,8 @@
             android:text="@string/settingslib_learn_more_text"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_gravity="start"
+            android:textAlignment="viewStart"
             android:paddingBottom="8dp"
             android:clickable="true"
             android:visibility="gone"
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
index 23aa993..dda7517c 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v31/settingslib_preference.xml
@@ -42,6 +42,8 @@
             android:id="@android:id/title"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_gravity="start"
+            android:textAlignment="viewStart"
             android:maxLines="2"
             android:textAppearance="?android:attr/textAppearanceListItem"
             android:ellipsize="marquee"/>
diff --git a/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
index 70ce374..fedcc77 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout-v33/settingslib_preference.xml
@@ -42,6 +42,8 @@
             android:id="@android:id/title"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_gravity="start"
+            android:textAlignment="viewStart"
             android:maxLines="2"
             android:hyphenationFrequency="normalFast"
             android:lineBreakWordStyle="phrase"
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml b/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml
index 6046d91..195d45f 100644
--- a/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml
+++ b/packages/SettingsLib/TopIntroPreference/res/layout-v33/top_intro_preference.xml
@@ -30,6 +30,8 @@
         android:id="@android:id/title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:layout_gravity="start"
+        android:textAlignment="viewStart"
         android:clickable="false"
         android:longClickable="false"
         android:maxLines="10"
diff --git a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
index 4d6e1b7..bee6bc7 100644
--- a/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
+++ b/packages/SettingsLib/TopIntroPreference/res/layout/top_intro_preference.xml
@@ -30,6 +30,8 @@
         android:id="@android:id/title"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:layout_gravity="start"
+        android:textAlignment="viewStart"
         android:clickable="false"
         android:longClickable="false"
         android:maxLines="10"
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index e846480..8d4aa9a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -67,6 +67,10 @@
     static final String STORAGE_MANAGER_ENABLED_PROPERTY =
             "ro.storage_manager.enabled";
 
+    @VisibleForTesting
+    static final String INCOMPATIBLE_CHARGER_WARNING_DISABLED =
+            "incompatible_charger_warning_disabled";
+
     private static Signature[] sSystemSignature;
     private static String sPermissionControllerPackageName;
     private static String sServicesSystemSharedLibPackageName;
@@ -652,6 +656,19 @@
 
     /** Whether there is any incompatible chargers in the current UsbPort? */
     public static boolean containsIncompatibleChargers(Context context, String tag) {
+        // Avoid the caller doesn't have permission to read the "Settings.Secure" data.
+        try {
+            // Whether the incompatible charger warning is disabled or not
+            if (Settings.Secure.getInt(context.getContentResolver(),
+                    INCOMPATIBLE_CHARGER_WARNING_DISABLED, 0) == 1) {
+                Log.d(tag, "containsIncompatibleChargers: disabled");
+                return false;
+            }
+        } catch (Exception e) {
+            Log.e(tag, "containsIncompatibleChargers()", e);
+            return false;
+        }
+
         final List<UsbPort> usbPortList =
                 context.getSystemService(UsbManager.class).getPorts();
         if (usbPortList == null || usbPortList.isEmpty()) {
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
index dce1e20..0637e5d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/UtilsTest.java
@@ -43,6 +43,7 @@
 import android.telephony.ServiceState;
 import android.text.TextUtils;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -65,6 +66,7 @@
 @Config(shadows = {UtilsTest.ShadowSecure.class, UtilsTest.ShadowLocationManager.class})
 public class UtilsTest {
     private static final double[] TEST_PERCENTAGES = {0, 0.4, 0.5, 0.6, 49, 49.3, 49.8, 50, 100};
+    private static final String TAG = "UtilsTest";
     private static final String PERCENTAGE_0 = "0%";
     private static final String PERCENTAGE_1 = "1%";
     private static final String PERCENTAGE_49 = "49%";
@@ -96,6 +98,12 @@
         mAudioManager = mContext.getSystemService(AudioManager.class);
     }
 
+    @After
+    public void reset() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Utils.INCOMPATIBLE_CHARGER_WARNING_DISABLED, 0);
+    }
+
     @Test
     public void testUpdateLocationEnabled() {
         int currentUserId = ActivityManager.getCurrentUser();
@@ -427,13 +435,13 @@
     @Test
     public void containsIncompatibleChargers_nullPorts_returnFalse() {
         when(mUsbManager.getPorts()).thenReturn(null);
-        assertThat(Utils.containsIncompatibleChargers(mContext, "tag")).isFalse();
+        assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
     }
 
     @Test
     public void containsIncompatibleChargers_emptyPorts_returnFalse() {
         when(mUsbManager.getPorts()).thenReturn(new ArrayList<>());
-        assertThat(Utils.containsIncompatibleChargers(mContext, "tag")).isFalse();
+        assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
     }
 
     @Test
@@ -443,13 +451,13 @@
         when(mUsbManager.getPorts()).thenReturn(usbPorts);
         when(mUsbPort.getStatus()).thenReturn(null);
 
-        assertThat(Utils.containsIncompatibleChargers(mContext, "tag")).isFalse();
+        assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
     }
 
     @Test
     public void containsIncompatibleChargers_returnTrue() {
         setupIncompatibleCharging();
-        assertThat(Utils.containsIncompatibleChargers(mContext, "tag")).isTrue();
+        assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isTrue();
     }
 
     @Test
@@ -457,7 +465,7 @@
         setupIncompatibleCharging();
         when(mUsbPortStatus.getComplianceWarnings()).thenReturn(new int[1]);
 
-        assertThat(Utils.containsIncompatibleChargers(mContext, "tag")).isFalse();
+        assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
     }
 
     @Test
@@ -465,7 +473,7 @@
         setupIncompatibleCharging();
         when(mUsbPort.supportsComplianceWarnings()).thenReturn(false);
 
-        assertThat(Utils.containsIncompatibleChargers(mContext, "tag")).isFalse();
+        assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
     }
 
     @Test
@@ -473,7 +481,16 @@
         setupIncompatibleCharging();
         when(mUsbPortStatus.isConnected()).thenReturn(false);
 
-        assertThat(Utils.containsIncompatibleChargers(mContext, "tag")).isFalse();
+        assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
+    }
+
+    @Test
+    public void containsIncompatibleChargers_disableWarning_returnFalse() {
+        setupIncompatibleCharging();
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Utils.INCOMPATIBLE_CHARGER_WARNING_DISABLED, 1);
+
+        assertThat(Utils.containsIncompatibleChargers(mContext, TAG)).isFalse();
     }
 
     private void setupIncompatibleCharging() {
diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/shared/page/SceneModule.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/ui/composable/SceneModule.kt
similarity index 94%
rename from packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/shared/page/SceneModule.kt
rename to packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/ui/composable/SceneModule.kt
index 18c9513..24064b1 100644
--- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/shared/page/SceneModule.kt
+++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/scene/ui/composable/SceneModule.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.scene.shared.page
+package com.android.systemui.scene.ui.composable
 
 import com.android.systemui.scene.shared.model.Scene
 import dagger.Module
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ui/composable/SceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ui/composable/SceneModule.kt
index 530706e..ee53ece 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ui/composable/SceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ui/composable/SceneModule.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.scene.ui.composable
 
 import com.android.systemui.bouncer.ui.composable.BouncerScene
-import com.android.systemui.keyguard.ui.composable.LockScreenScene
+import com.android.systemui.keyguard.ui.composable.LockscreenScene
 import com.android.systemui.qs.ui.composable.QuickSettingsScene
 import com.android.systemui.scene.shared.model.Scene
 import com.android.systemui.shade.ui.composable.ShadeScene
@@ -30,7 +30,7 @@
     fun scenes(
         bouncer: BouncerScene,
         gone: GoneScene,
-        lockScreen: LockScreenScene,
+        lockScreen: LockscreenScene,
         qs: QuickSettingsScene,
         shade: ShadeScene,
     ): Set<Scene> {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 6f6d0f9..f48bab9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -55,11 +55,13 @@
 class BouncerScene
 @Inject
 constructor(
-    private val viewModel: BouncerViewModel,
+    private val viewModelFactory: BouncerViewModel.Factory,
 ) : ComposableScene {
     override val key = SceneKey.Bouncer
 
-    override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
+    override fun destinationScenes(
+        containerName: String,
+    ): StateFlow<Map<UserAction, SceneModel>> =
         MutableStateFlow<Map<UserAction, SceneModel>>(
                 mapOf(
                     UserAction.Back to SceneModel(SceneKey.Lockscreen),
@@ -67,7 +69,11 @@
             )
             .asStateFlow()
 
-    @Composable override fun Content(modifier: Modifier) = BouncerScene(viewModel, modifier)
+    @Composable
+    override fun Content(
+        containerName: String,
+        modifier: Modifier,
+    ) = BouncerScene(viewModelFactory.create(containerName), modifier)
 }
 
 @Composable
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
index ab7bc26..7c07a8b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenScene.kt
@@ -52,25 +52,32 @@
 @Inject
 constructor(
     @Application private val applicationScope: CoroutineScope,
-    private val viewModel: LockscreenSceneViewModel,
+    private val viewModelFactory: LockscreenSceneViewModel.Factory,
 ) : ComposableScene {
     override val key = SceneKey.Lockscreen
 
-    override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
-        viewModel.upDestinationSceneKey
-            .map { pageKey -> destinationScenes(up = pageKey) }
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.Eagerly,
-                initialValue = destinationScenes(up = viewModel.upDestinationSceneKey.value)
-            )
+    private var unsafeViewModel: LockscreenSceneViewModel? = null
+
+    override fun destinationScenes(
+        containerName: String,
+    ): StateFlow<Map<UserAction, SceneModel>> =
+        getOrCreateViewModelSingleton(containerName).let { viewModel ->
+            viewModel.upDestinationSceneKey
+                .map { pageKey -> destinationScenes(up = pageKey) }
+                .stateIn(
+                    scope = applicationScope,
+                    started = SharingStarted.Eagerly,
+                    initialValue = destinationScenes(up = viewModel.upDestinationSceneKey.value)
+                )
+        }
 
     @Composable
     override fun Content(
+        containerName: String,
         modifier: Modifier,
     ) {
         LockscreenScene(
-            viewModel = viewModel,
+            viewModel = getOrCreateViewModelSingleton(containerName),
             modifier = modifier,
         )
     }
@@ -83,6 +90,13 @@
             UserAction.Swipe(Direction.DOWN) to SceneModel(SceneKey.Shade)
         )
     }
+
+    private fun getOrCreateViewModelSingleton(
+        containerName: String,
+    ): LockscreenSceneViewModel {
+        return unsafeViewModel
+            ?: viewModelFactory.create(containerName).also { unsafeViewModel = it }
+    }
 }
 
 @Composable
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
index 130395a..58db37e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/ui/composable/QuickSettingsScene.kt
@@ -44,11 +44,13 @@
 class QuickSettingsScene
 @Inject
 constructor(
-    private val viewModel: QuickSettingsSceneViewModel,
+    private val viewModelFactory: QuickSettingsSceneViewModel.Factory,
 ) : ComposableScene {
     override val key = SceneKey.QuickSettings
 
-    override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
+    override fun destinationScenes(
+        containerName: String,
+    ): StateFlow<Map<UserAction, SceneModel>> =
         MutableStateFlow<Map<UserAction, SceneModel>>(
                 mapOf(
                     UserAction.Swipe(Direction.UP) to SceneModel(SceneKey.Shade),
@@ -58,10 +60,11 @@
 
     @Composable
     override fun Content(
+        containerName: String,
         modifier: Modifier,
     ) {
         QuickSettingsScene(
-            viewModel = viewModel,
+            viewModel = viewModelFactory.create(containerName),
             modifier = modifier,
         )
     }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt
index a213666..6f3363e 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/ComposableScene.kt
@@ -22,5 +22,5 @@
 
 /** Compose-capable extension of [Scene]. */
 interface ComposableScene : Scene {
-    @Composable fun Content(modifier: Modifier)
+    @Composable fun Content(containerName: String, modifier: Modifier)
 }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
index 0070552..b387463 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/GoneScene.kt
@@ -41,7 +41,9 @@
 class GoneScene @Inject constructor() : ComposableScene {
     override val key = SceneKey.Gone
 
-    override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
+    override fun destinationScenes(
+        containerName: String,
+    ): StateFlow<Map<UserAction, SceneModel>> =
         MutableStateFlow<Map<UserAction, SceneModel>>(
                 mapOf(
                     UserAction.Swipe(Direction.DOWN) to SceneModel(SceneKey.Shade),
@@ -51,6 +53,7 @@
 
     @Composable
     override fun Content(
+        containerName: String,
         modifier: Modifier,
     ) {
         /*
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index f8a73d5..5e07610 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -75,6 +75,7 @@
             if (key == currentSceneKey) {
                 Scene(
                     scene = composableScene,
+                    containerName = viewModel.containerName,
                     onSceneChanged = viewModel::setCurrentScene,
                     modifier = Modifier.fillMaxSize(),
                 )
@@ -87,6 +88,7 @@
 @Composable
 private fun Scene(
     scene: ComposableScene,
+    containerName: String,
     onSceneChanged: (SceneModel) -> Unit,
     modifier: Modifier = Modifier,
 ) {
@@ -97,11 +99,12 @@
             modifier = Modifier.align(Alignment.Center),
         ) {
             scene.Content(
+                containerName = containerName,
                 modifier = Modifier,
             )
 
             val destinationScenes: Map<UserAction, SceneModel> by
-                scene.destinationScenes().collectAsState()
+                scene.destinationScenes(containerName).collectAsState()
             val swipeLeftDestinationScene = destinationScenes[UserAction.Swipe(Direction.LEFT)]
             val swipeUpDestinationScene = destinationScenes[UserAction.Swipe(Direction.UP)]
             val swipeRightDestinationScene = destinationScenes[UserAction.Swipe(Direction.RIGHT)]
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
index 5a09204..e4513d0 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt
@@ -48,25 +48,32 @@
 @Inject
 constructor(
     @Application private val applicationScope: CoroutineScope,
-    private val viewModel: ShadeSceneViewModel,
+    private val viewModelFactory: ShadeSceneViewModel.Factory,
 ) : ComposableScene {
     override val key = SceneKey.Shade
 
-    override fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
-        viewModel.upDestinationSceneKey
-            .map { sceneKey -> destinationScenes(up = sceneKey) }
-            .stateIn(
-                scope = applicationScope,
-                started = SharingStarted.Eagerly,
-                initialValue = destinationScenes(up = viewModel.upDestinationSceneKey.value),
-            )
+    private var unsafeViewModel: ShadeSceneViewModel? = null
+
+    override fun destinationScenes(
+        containerName: String,
+    ): StateFlow<Map<UserAction, SceneModel>> =
+        getOrCreateViewModelSingleton(containerName).let { viewModel ->
+            viewModel.upDestinationSceneKey
+                .map { sceneKey -> destinationScenes(up = sceneKey) }
+                .stateIn(
+                    scope = applicationScope,
+                    started = SharingStarted.Eagerly,
+                    initialValue = destinationScenes(up = viewModel.upDestinationSceneKey.value),
+                )
+        }
 
     @Composable
     override fun Content(
+        containerName: String,
         modifier: Modifier,
     ) {
         ShadeScene(
-            viewModel = viewModel,
+            viewModel = getOrCreateViewModelSingleton(containerName),
             modifier = modifier,
         )
     }
@@ -79,6 +86,13 @@
             UserAction.Swipe(Direction.DOWN) to SceneModel(SceneKey.QuickSettings),
         )
     }
+
+    private fun getOrCreateViewModelSingleton(
+        containerName: String,
+    ): ShadeSceneViewModel {
+        return unsafeViewModel
+            ?: viewModelFactory.create(containerName).also { unsafeViewModel = it }
+    }
 }
 
 @Composable
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt
index 2dd146c5..a4b1cee 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/WeatherData.kt
@@ -1,6 +1,7 @@
 package com.android.systemui.plugins
 
 import android.os.Bundle
+import android.util.Log
 import androidx.annotation.VisibleForTesting
 
 class WeatherData
@@ -11,6 +12,7 @@
     val temperature: Int,
 ) {
     companion object {
+        const val DEBUG = true
         private const val TAG = "WeatherData"
         @VisibleForTesting const val DESCRIPTION_KEY = "description"
         @VisibleForTesting const val STATE_KEY = "state"
@@ -23,20 +25,29 @@
             val state =
                 WeatherStateIcon.fromInt(extras.getInt(STATE_KEY, INVALID_WEATHER_ICON_STATE))
             val temperature = readIntFromBundle(extras, TEMPERATURE_KEY)
-            return if (
+            if (
                 description == null ||
                     state == null ||
                     !extras.containsKey(USE_CELSIUS_KEY) ||
                     temperature == null
-            )
-                null
-            else
-                WeatherData(
-                    description = description,
-                    state = state,
-                    useCelsius = extras.getBoolean(USE_CELSIUS_KEY),
-                    temperature = temperature
-                )
+            ) {
+                if (DEBUG) {
+                    Log.w(TAG, "Weather data did not parse from $extras")
+                }
+                return null
+            } else {
+                val result =
+                    WeatherData(
+                        description = description,
+                        state = state,
+                        useCelsius = extras.getBoolean(USE_CELSIUS_KEY),
+                        temperature = temperature
+                    )
+                if (DEBUG) {
+                    Log.i(TAG, "Weather data parsed $result from $extras")
+                }
+                return result
+            }
         }
 
         private fun readIntFromBundle(extras: Bundle, key: String): Int? =
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_security_container_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_security_container_view.xml
index 751b07a..dc58d50 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_security_container_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_security_container_view.xml
@@ -19,9 +19,7 @@
 
 <com.android.keyguard.KeyguardSecurityContainer
     xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/keyguard_security_container"
-    android:background="?androidprv:attr/materialColorSurfaceContainer"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:clipChildren="false"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 7dc8afe..003f9b0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -295,9 +295,8 @@
     <string name="screenrecord_save_title">Screen recording saved</string>
     <!-- Subtext for a notification shown after saving a screen recording to prompt the user to view it [CHAR_LIMIT=100] -->
     <string name="screenrecord_save_text">Tap to view</string>
-    <!-- A toast message shown when there is an error deleting a screen recording [CHAR LIMIT=NONE] -->
-    <string name="screenrecord_delete_error">Error deleting screen recording</string>
-    <!-- A toast message shown when the screen recording cannot be started due to insufficient permissions [CHAR LIMIT=NONE] -->
+    <!-- A toast message shown when there is an error saving a screen recording [CHAR LIMIT=NONE] -->
+    <string name="screenrecord_save_error">Error saving screen recording</string>
     <!-- A toast message shown when the screen recording cannot be started due to a generic error [CHAR LIMIT=NONE] -->
     <string name="screenrecord_start_error">Error starting screen recording</string>
 
diff --git a/packages/SystemUI/res/xml/media_session_collapsed.xml b/packages/SystemUI/res/xml/media_session_collapsed.xml
index 5129fc0..c053b33 100644
--- a/packages/SystemUI/res/xml/media_session_collapsed.xml
+++ b/packages/SystemUI/res/xml/media_session_collapsed.xml
@@ -63,8 +63,8 @@
         android:layout_marginEnd="@dimen/qs_media_padding"
         app:layout_constraintEnd_toStartOf="@id/action_button_guideline"
         app:layout_constrainedWidth="true"
-        app:layout_constraintTop_toBottomOf="@id/icon"
         app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/header_artist"
         app:layout_constraintHorizontal_bias="0" />
 
     <Constraint
@@ -87,11 +87,12 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_marginEnd="@dimen/qs_media_padding"
+        android:layout_marginBottom="@dimen/qs_media_padding"
         android:layout_marginTop="0dp"
         app:layout_constraintEnd_toStartOf="@id/action_button_guideline"
         app:layout_constrainedWidth="true"
-        app:layout_constraintTop_toBottomOf="@id/header_title"
         app:layout_constraintStart_toEndOf="@id/media_explicit_indicator"
+        app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintVertical_bias="0" />
 
     <Constraint
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 1fbf743..74c325d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -352,7 +352,8 @@
 
         @Override public TaskSnapshot screenshotTask(int taskId) {
             try {
-                return ActivityTaskManager.getService().takeTaskSnapshot(taskId);
+                return ActivityTaskManager.getService().takeTaskSnapshot(taskId,
+                        true /* updateCache */);
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to screenshot task", e);
             }
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index a8f2804..8ea4c31a 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -23,6 +23,7 @@
 import android.content.res.Resources
 import android.text.format.DateFormat
 import android.util.TypedValue
+import android.util.Log
 import android.view.View
 import android.view.View.OnAttachStateChangeListener
 import android.view.ViewTreeObserver
@@ -101,7 +102,12 @@
                 }
                 updateFontSizes()
                 updateTimeListeners()
-                cachedWeatherData?.let { value.events.onWeatherDataChanged(it) }
+                cachedWeatherData?.let {
+                    if (WeatherData.DEBUG) {
+                        Log.i(TAG, "Pushing cached weather data to new clock: $it")
+                    }
+                    value.events.onWeatherDataChanged(it)
+                }
                 value.smallClock.view.addOnAttachStateChangeListener(
                     object : OnAttachStateChangeListener {
                         override fun onViewAttachedToWindow(p0: View?) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 794eeda..1db0ab6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -315,6 +315,14 @@
     }
 
     /**
+     * Returns true if the large clock will block the notification shelf in AOD
+     */
+    public boolean isLargeClockBlockingNotificationShelf() {
+        ClockController clock = mKeyguardClockSwitchController.getClock();
+        return clock != null && clock.getLargeClock().getConfig().getHasCustomWeatherDataDisplay();
+    }
+
+    /**
      * Updates the alignment of the KeyguardStatusView and animates the transition if requested.
      */
     public void updateAlignment(
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 3add8c8..cabe900 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -115,7 +115,7 @@
     private var overlayTouchListener: TouchExplorationStateChangeListener? = null
 
     private val coreLayoutParams = WindowManager.LayoutParams(
-        WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
+        WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
         0 /* flags set in computeLayoutParams() */,
         PixelFormat.TRANSLUCENT
     ).apply {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index eaa8ed5..c6528d0 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -20,6 +20,8 @@
 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
 import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
 import com.android.systemui.dagger.qualifiers.Application
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.SharingStarted
@@ -34,7 +36,7 @@
     @Application private val applicationContext: Context,
     @Application private val applicationScope: CoroutineScope,
     interactorFactory: BouncerInteractor.Factory,
-    containerName: String,
+    @Assisted containerName: String,
 ) {
     private val interactor: BouncerInteractor = interactorFactory.create(containerName)
 
@@ -94,4 +96,11 @@
             else -> null
         }
     }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(
+            containerName: String,
+        ): BouncerViewModel
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index 691017b..b2bcb05 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -232,8 +232,7 @@
 
         // check for false tap if it is a seekbar interaction
         if (interactionType == MEDIA_SEEKBAR) {
-            localResult[0] &= isFalseTap(mFeatureFlags.isEnabled(Flags.MEDIA_FALSING_PENALTY)
-                    ? FalsingManager.MODERATE_PENALTY : FalsingManager.LOW_PENALTY);
+            localResult[0] &= isFalseTap(FalsingManager.MODERATE_PENALTY);
         }
 
         logDebug("False Gesture (type: " + interactionType + "): " + localResult[0]);
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index f71a46c..c8485dc 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -88,8 +88,7 @@
     // TODO(b/278873737): Tracking Bug
     @JvmField
     val LOAD_NOTIFICATIONS_BEFORE_THE_USER_SWITCH_IS_COMPLETE =
-            unreleasedFlag(278873737, "load_notifications_before_the_user_switch_is_complete",
-                    teamfood = true)
+            releasedFlag(278873737, "load_notifications_before_the_user_switch_is_complete")
 
     // TODO(b/277338665): Tracking Bug
     @JvmField
@@ -392,8 +391,6 @@
     // TODO(b/254513168): Tracking Bug
     @JvmField val UMO_SURFACE_RIPPLE = releasedFlag(907, "umo_surface_ripple")
 
-    @JvmField val MEDIA_FALSING_PENALTY = releasedFlag(908, "media_falsing_media")
-
     // TODO(b/261734857): Tracking Bug
     @JvmField val UMO_TURBULENCE_NOISE = releasedFlag(909, "umo_turbulence_noise")
 
@@ -720,8 +717,4 @@
     @JvmField
     val SPLIT_SHADE_SUBPIXEL_OPTIMIZATION =
             unreleasedFlag(2805, "split_shade_subpixel_optimization", teamfood = true)
-
-    // TODO(b/278761837): Tracking Bug
-    @JvmField
-    val USE_NEW_ACTIVITY_STARTER = releasedFlag(2801, name = "use_new_activity_starter")
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt
index 5e806b6..1f421fa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinator.kt
@@ -21,41 +21,44 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyboard.backlight.ui.view.KeyboardBacklightDialog
+import com.android.systemui.keyboard.backlight.ui.viewmodel.BacklightDialogContentViewModel
 import com.android.systemui.keyboard.backlight.ui.viewmodel.BacklightDialogViewModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
 
+private fun defaultCreateDialog(context: Context): (Int, Int) -> KeyboardBacklightDialog {
+    return { currentLevel: Int, maxLevel: Int ->
+        KeyboardBacklightDialog(context, currentLevel, maxLevel)
+    }
+}
+
 /**
  * Based on the state produced from [BacklightDialogViewModel] shows or hides keyboard backlight
  * indicator
  */
 @SysUISingleton
 class KeyboardBacklightDialogCoordinator
-@Inject
 constructor(
     @Application private val applicationScope: CoroutineScope,
-    private val context: Context,
     private val viewModel: BacklightDialogViewModel,
+    private val createDialog: (Int, Int) -> KeyboardBacklightDialog
 ) {
 
+    @Inject
+    constructor(
+        @Application applicationScope: CoroutineScope,
+        context: Context,
+        viewModel: BacklightDialogViewModel
+    ) : this(applicationScope, viewModel, defaultCreateDialog(context))
+
     var dialog: KeyboardBacklightDialog? = null
 
     fun startListening() {
         applicationScope.launch {
-            viewModel.dialogContent.collect { dialogViewModel ->
-                if (dialogViewModel != null) {
-                    if (dialog == null) {
-                        dialog =
-                            KeyboardBacklightDialog(
-                                context,
-                                initialCurrentLevel = dialogViewModel.currentValue,
-                                initialMaxLevel = dialogViewModel.maxValue
-                            )
-                        dialog?.show()
-                    } else {
-                        dialog?.updateState(dialogViewModel.currentValue, dialogViewModel.maxValue)
-                    }
+            viewModel.dialogContent.collect { contentModel ->
+                if (contentModel != null) {
+                    showDialog(contentModel)
                 } else {
                     dialog?.dismiss()
                     dialog = null
@@ -63,4 +66,15 @@
             }
         }
     }
+
+    private fun showDialog(model: BacklightDialogContentViewModel) {
+        if (dialog == null) {
+            dialog = createDialog(model.currentValue, model.maxValue)
+        } else {
+            dialog?.updateState(model.currentValue, model.maxValue)
+        }
+        // let's always show dialog - even if we're just updating it, it might have been dismissed
+        // externally by tapping finger outside of it
+        dialog?.show()
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
index 5079487..1469d96 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
@@ -786,10 +786,10 @@
 
         // Song name
         var song: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE)
-        if (song == null) {
+        if (song.isNullOrBlank()) {
             song = metadata?.getString(MediaMetadata.METADATA_KEY_TITLE)
         }
-        if (song == null) {
+        if (song.isNullOrBlank()) {
             song = HybridGroupManager.resolveTitle(notif)
         }
         if (song.isNullOrBlank()) {
@@ -846,7 +846,7 @@
 
         // Artist name
         var artist: CharSequence? = metadata?.getString(MediaMetadata.METADATA_KEY_ARTIST)
-        if (artist == null) {
+        if (artist.isNullOrBlank()) {
             artist = HybridGroupManager.resolveText(notif)
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index f8e3ecb..32a7935 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -617,10 +617,7 @@
         seamlessView.setContentDescription(deviceString);
         seamlessView.setOnClickListener(
                 v -> {
-                    if (mFalsingManager.isFalseTap(
-                            mFeatureFlags.isEnabled(Flags.MEDIA_FALSING_PENALTY)
-                                    ? FalsingManager.MODERATE_PENALTY :
-                                    FalsingManager.LOW_PENALTY)) {
+                    if (mFalsingManager.isFalseTap(FalsingManager.MODERATE_PENALTY)) {
                         return;
                     }
 
@@ -1130,10 +1127,7 @@
             } else {
                 button.setEnabled(true);
                 button.setOnClickListener(v -> {
-                    if (!mFalsingManager.isFalseTap(
-                            mFeatureFlags.isEnabled(Flags.MEDIA_FALSING_PENALTY)
-                                    ? FalsingManager.MODERATE_PENALTY :
-                                    FalsingManager.LOW_PENALTY)) {
+                    if (!mFalsingManager.isFalseTap(FalsingManager.MODERATE_PENALTY)) {
                         mLogger.logTapAction(button.getId(), mUid, mPackageName, mInstanceId);
                         logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT);
                         action.run();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index b476521..b936c41 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -522,7 +522,7 @@
         return mExpanded;
     }
 
-    void addTile(QSPanelControllerBase.TileRecord tileRecord) {
+    final void addTile(QSPanelControllerBase.TileRecord tileRecord) {
         final QSTile.Callback callback = new QSTile.Callback() {
             @Override
             public void onStateChanged(QSTile.State state) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index fdab9b1..20f0352 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -199,7 +199,7 @@
         mMediaHost.removeVisibilityChangeListener(mMediaHostVisibilityListener);
 
         for (TileRecord record : mRecords) {
-            record.tile.removeCallbacks();
+            record.tile.removeCallback(record.callback);
         }
         mRecords.clear();
         mDumpManager.unregisterDumpable(mView.getDumpableTag());
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
index 91c6e8b..c579f5c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractor.kt
@@ -318,7 +318,6 @@
             // We have a handful of different cases
             qsTile !is CustomTile -> {
                 // The tile is not a custom tile. Make sure they are reset to the correct user
-                qsTile.removeCallbacks()
                 if (userChanged) {
                     qsTile.userSwitch(user)
                     logger.logTileUserChanged(tileSpec, user)
@@ -327,7 +326,6 @@
             }
             qsTile.user == user -> {
                 // The tile is a custom tile for the same user, just return it
-                qsTile.removeCallbacks()
                 qsTile
             }
             else -> {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
index 0ed8b21..6d7455f 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.scene
 
-import com.android.systemui.scene.shared.page.SceneModule
+import com.android.systemui.scene.ui.composable.SceneModule
 import dagger.Module
 
 @Module(
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
index 435ff4b..354de8a 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/Scene.kt
@@ -59,7 +59,7 @@
      * The API is designed such that it's possible to emit ever-changing values for each
      * [UserAction] to enable, disable, or change the destination scene of a given user action.
      */
-    fun destinationScenes(): StateFlow<Map<UserAction, SceneModel>> =
+    fun destinationScenes(containerName: String): StateFlow<Map<UserAction, SceneModel>> =
         MutableStateFlow(emptyMap<UserAction, SceneModel>()).asStateFlow()
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
index afc0531..a4daafc 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModel.kt
@@ -29,7 +29,7 @@
 @AssistedInject
 constructor(
     private val interactor: SceneInteractor,
-    @Assisted private val containerName: String,
+    @Assisted val containerName: String,
 ) {
     /**
      * Keys of all scenes in the container.
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
index 84f358c..e1ac0fd 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingService.java
@@ -453,9 +453,9 @@
                 postGroupNotification(currentUser);
                 mNotificationManager.notifyAsUser(null, mNotificationId,  notification,
                         currentUser);
-            } catch (IOException e) {
+            } catch (IOException | IllegalStateException e) {
                 Log.e(TAG, "Error saving screen recording: " + e.getMessage());
-                showErrorToast(R.string.screenrecord_delete_error);
+                showErrorToast(R.string.screenrecord_save_error);
                 mNotificationManager.cancelAsUser(null, mNotificationId, currentUser);
             }
         });
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
index b8d96f7..b80a01212 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java
@@ -52,8 +52,9 @@
 import android.view.WindowManager;
 
 import com.android.systemui.media.MediaProjectionCaptureTarget;
-import java.io.File;
+
 import java.io.Closeable;
+import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.file.Files;
@@ -321,7 +322,7 @@
     /**
      * Store recorded video
      */
-    protected SavedRecording save() throws IOException {
+    protected SavedRecording save() throws IOException, IllegalStateException {
         String fileName = new SimpleDateFormat("'screen-'yyyyMMdd-HHmmss'.mp4'")
                 .format(new Date());
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingMuxer.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingMuxer.java
index 7ffcfd4..dc3310d 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingMuxer.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordingMuxer.java
@@ -52,9 +52,8 @@
     /**
      * RUN IN THE BACKGROUND THREAD!
      */
-    public void mux() throws IOException {
-        MediaMuxer muxer = null;
-        muxer = new MediaMuxer(mOutFile, mFormat);
+    public void mux() throws IOException, IllegalStateException {
+        MediaMuxer muxer = new MediaMuxer(mOutFile, mFormat);
         // Add extractors
         for (String file: mFiles) {
             MediaExtractor extractor = new MediaExtractor();
@@ -74,7 +73,10 @@
             }
         }
 
+        // This may throw IllegalStateException if no tracks were added above
+        // Let the error propagate up so we can notify the user.
         muxer.start();
+
         for (Pair<MediaExtractor, Integer> pair: mExtractorIndexToMuxerIndex.keySet()) {
             MediaExtractor extractor = pair.first;
             extractor.selectTrack(pair.second);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
index 67e9a87..2e47ab6 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
@@ -48,7 +48,9 @@
     }
 
     override suspend fun captureTask(taskId: Int): Bitmap? {
-        val snapshot = withContext(bgContext) { atmService.takeTaskSnapshot(taskId) } ?: return null
+        val snapshot = withContext(bgContext) {
+            atmService.takeTaskSnapshot(taskId, false /* updateCache */)
+        } ?: return null
         return Bitmap.wrapHardwareBuffer(snapshot.hardwareBuffer, snapshot.colorSpace)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index fbf134d..5fb3c01 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -166,21 +166,19 @@
             }
 
             override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
-                backgroundHandler.run {
-                    handleUserSwitching(newUserId)
-                    reply?.sendResult(null)
-                }
+                handleUserSwitching(newUserId)
+                reply?.sendResult(null)
             }
 
             override fun onUserSwitchComplete(newUserId: Int) {
-                backgroundHandler.run {
-                    handleUserSwitchComplete(newUserId)
-                }
+                handleUserSwitchComplete(newUserId)
             }
         }, TAG)
     }
 
+    @WorkerThread
     protected open fun handleBeforeUserSwitching(newUserId: Int) {
+        Assert.isNotMainThread()
         setUserIdInternal(newUserId)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 0fdd7ca..784a360 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -1570,6 +1570,12 @@
             // When media is visible, it overlaps with the large clock. Use small clock instead.
             return SMALL;
         }
+        // To prevent the weather clock from overlapping with the notification shelf on AOD, we use
+        // the small clock here
+        if (mKeyguardStatusViewController.isLargeClockBlockingNotificationShelf()
+                && hasVisibleNotifications() && isOnAod()) {
+            return SMALL;
+        }
         return LARGE;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 9ede6ce..ed8050a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -140,7 +140,13 @@
     }
 
     protected boolean shouldHeadsUpBecomePinned(@NonNull NotificationEntry entry) {
-        return hasFullScreenIntent(entry);
+        final HeadsUpEntry headsUpEntry = getHeadsUpEntry(entry.getKey());
+        if (headsUpEntry == null) {
+            // This should not happen since shouldHeadsUpBecomePinned is always called after adding
+            // the NotificationEntry into AlertingNotificationManager's mAlertEntries map.
+            return hasFullScreenIntent(entry);
+        }
+        return hasFullScreenIntent(entry) && !headsUpEntry.wasUnpinned;
     }
 
     protected boolean hasFullScreenIntent(@NonNull NotificationEntry entry) {
@@ -151,6 +157,9 @@
             @NonNull HeadsUpManager.HeadsUpEntry headsUpEntry, boolean isPinned) {
         mLogger.logSetEntryPinned(headsUpEntry.mEntry, isPinned);
         NotificationEntry entry = headsUpEntry.mEntry;
+        if (!isPinned) {
+            headsUpEntry.wasUnpinned = true;
+        }
         if (entry.isRowPinned() != isPinned) {
             entry.setRowPinned(isPinned);
             updatePinnedMode();
@@ -177,7 +186,9 @@
     protected void onAlertEntryAdded(AlertEntry alertEntry) {
         NotificationEntry entry = alertEntry.mEntry;
         entry.setHeadsUp(true);
-        setEntryPinned((HeadsUpEntry) alertEntry, shouldHeadsUpBecomePinned(entry));
+
+        final boolean shouldPin = shouldHeadsUpBecomePinned(entry);
+        setEntryPinned((HeadsUpEntry) alertEntry, shouldPin);
         EventLogTags.writeSysuiHeadsUpStatus(entry.getKey(), 1 /* visible */);
         for (OnHeadsUpChangedListener listener : mListeners) {
             listener.onHeadsUpStateChanged(entry, true);
@@ -411,6 +422,7 @@
     protected class HeadsUpEntry extends AlertEntry {
         public boolean remoteInputActive;
         protected boolean expanded;
+        protected boolean wasUnpinned;
 
         @Override
         public boolean isSticky() {
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
index c72853e..7b652c1 100644
--- a/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/SysUIConcurrencyModule.java
@@ -29,18 +29,28 @@
 import com.android.systemui.dagger.qualifiers.LongRunning;
 import com.android.systemui.dagger.qualifiers.Main;
 
+import dagger.Module;
+import dagger.Provides;
+
 import java.util.concurrent.Executor;
 
 import javax.inject.Named;
 
-import dagger.Module;
-import dagger.Provides;
-
 /**
  * Dagger Module for classes found within the concurrent package.
  */
 @Module
 public abstract class SysUIConcurrencyModule {
+
+    // Slow BG executor can potentially affect UI if UI is waiting for an updated state from this
+    // thread
+    private static final Long BG_SLOW_DISPATCH_THRESHOLD = 1000L;
+    private static final Long BG_SLOW_DELIVERY_THRESHOLD = 1000L;
+    private static final Long LONG_SLOW_DISPATCH_THRESHOLD = 2500L;
+    private static final Long LONG_SLOW_DELIVERY_THRESHOLD = 2500L;
+    private static final Long BROADCAST_SLOW_DISPATCH_THRESHOLD = 1000L;
+    private static final Long BROADCAST_SLOW_DELIVERY_THRESHOLD = 1000L;
+
     /** Background Looper */
     @Provides
     @SysUISingleton
@@ -49,6 +59,8 @@
         HandlerThread thread = new HandlerThread("SysUiBg",
                 Process.THREAD_PRIORITY_BACKGROUND);
         thread.start();
+        thread.getLooper().setSlowLogThresholdMs(BG_SLOW_DISPATCH_THRESHOLD,
+                BG_SLOW_DELIVERY_THRESHOLD);
         return thread.getLooper();
     }
 
@@ -60,6 +72,8 @@
         HandlerThread thread = new HandlerThread("BroadcastRunning",
                 Process.THREAD_PRIORITY_BACKGROUND);
         thread.start();
+        thread.getLooper().setSlowLogThresholdMs(BROADCAST_SLOW_DISPATCH_THRESHOLD,
+                BROADCAST_SLOW_DELIVERY_THRESHOLD);
         return thread.getLooper();
     }
 
@@ -71,6 +85,8 @@
         HandlerThread thread = new HandlerThread("SysUiLng",
                 Process.THREAD_PRIORITY_BACKGROUND);
         thread.start();
+        thread.getLooper().setSlowLogThresholdMs(LONG_SLOW_DISPATCH_THRESHOLD,
+                LONG_SLOW_DELIVERY_THRESHOLD);
         return thread.getLooper();
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
index 4cb99a2..6afbde0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
@@ -120,7 +120,6 @@
                 gestureCompleteListenerCaptor.capture());
 
         mGestureFinalizedListener = gestureCompleteListenerCaptor.getValue();
-        mFakeFeatureFlags.set(Flags.MEDIA_FALSING_PENALTY, true);
         mFakeFeatureFlags.set(Flags.FALSING_OFF_FOR_UNFOLDED, true);
     }
 
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
new file mode 100644
index 0000000..7207fbf
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/KeyboardBacklightDialogCoordinatorTest.kt
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyboard.backlight.ui
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyboard.backlight.domain.interactor.KeyboardBacklightInteractor
+import com.android.systemui.keyboard.backlight.ui.view.KeyboardBacklightDialog
+import com.android.systemui.keyboard.backlight.ui.viewmodel.BacklightDialogViewModel
+import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
+import com.android.systemui.keyboard.shared.model.BacklightModel
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyboardBacklightDialogCoordinatorTest : SysuiTestCase() {
+
+    @Mock private lateinit var accessibilityManagerWrapper: AccessibilityManagerWrapper
+    @Mock private lateinit var dialog: KeyboardBacklightDialog
+
+    private val keyboardRepository = FakeKeyboardRepository()
+    private lateinit var underTest: KeyboardBacklightDialogCoordinator
+    private val timeoutMillis = 3000L
+    private val testScope = TestScope(StandardTestDispatcher())
+
+    private val createDialog = { value: Int, maxValue: Int ->
+        dialogCreationValue = value
+        dialogCreationMaxValue = maxValue
+        dialog
+    }
+    private var dialogCreationValue = -1
+    private var dialogCreationMaxValue = -1
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        whenever(accessibilityManagerWrapper.getRecommendedTimeoutMillis(any(), any()))
+            .thenReturn(timeoutMillis.toInt())
+        val viewModel =
+            BacklightDialogViewModel(
+                KeyboardBacklightInteractor(keyboardRepository),
+                accessibilityManagerWrapper
+            )
+        underTest =
+            KeyboardBacklightDialogCoordinator(testScope.backgroundScope, viewModel, createDialog)
+        underTest.startListening()
+        keyboardRepository.setIsAnyKeyboardConnected(true)
+    }
+
+    @Test
+    fun showsDialog_afterBacklightChange() =
+        testScope.runTest {
+            setBacklightValue(1)
+
+            verify(dialog).show()
+        }
+
+    @Test
+    fun updatesDialog_withLatestValues_afterBacklightChange() =
+        testScope.runTest {
+            setBacklightValue(value = 1, maxValue = 5)
+            setBacklightValue(value = 2, maxValue = 5)
+
+            verify(dialog).updateState(2, 5)
+        }
+
+    @Test
+    fun showsDialog_withDataFromBacklightChange() =
+        testScope.runTest {
+            setBacklightValue(value = 4, maxValue = 5)
+
+            Truth.assertThat(dialogCreationValue).isEqualTo(4)
+            Truth.assertThat(dialogCreationMaxValue).isEqualTo(5)
+        }
+
+    @Test
+    fun dismissesDialog_afterTimeout() =
+        testScope.runTest {
+            setBacklightValue(1)
+
+            advanceTimeBy(timeoutMillis + 1)
+
+            verify(dialog).dismiss()
+        }
+
+    @Test
+    fun dismissesDialog_onlyAfterTimeout_fromLastBacklightChange() =
+        testScope.runTest {
+            setBacklightValue(1)
+            advanceTimeBy(timeoutMillis * 2 / 3)
+            // majority of timeout passed
+
+            // this should restart timeout
+            setBacklightValue(2)
+            advanceTimeBy(timeoutMillis * 2 / 3)
+            verify(dialog, never()).dismiss()
+
+            advanceTimeBy(timeoutMillis * 2 / 3)
+            // finally timeout reached and dialog was dismissed
+            verify(dialog, times(1)).dismiss()
+        }
+
+    @Test
+    fun showsDialog_ifItWasAlreadyShownAndDismissedBySomethingElse() =
+        testScope.runTest {
+            setBacklightValue(1)
+            // let's pretend dialog is dismissed e.g. by user tapping on the screen
+            whenever(dialog.isShowing).thenReturn(false)
+
+            // no advancing time, we're still in timeout period
+            setBacklightValue(2)
+
+            verify(dialog, times(2)).show()
+        }
+
+    private fun TestScope.setBacklightValue(value: Int, maxValue: Int = MAX_BACKLIGHT) {
+        keyboardRepository.setBacklight(BacklightModel(value, maxValue))
+        runCurrent()
+    }
+
+    private companion object {
+        const val MAX_BACKLIGHT = 5
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModelTest.kt
deleted file mode 100644
index 1fec5a4..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/backlight/ui/viewmodel/BacklightDialogViewModelTest.kt
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package com.android.systemui.keyboard.backlight.ui.viewmodel
-
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.keyboard.backlight.domain.interactor.KeyboardBacklightInteractor
-import com.android.systemui.keyboard.data.repository.FakeKeyboardRepository
-import com.android.systemui.keyboard.shared.model.BacklightModel
-import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.test.advanceTimeBy
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
-import org.mockito.Mock
-import org.mockito.MockitoAnnotations
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(JUnit4::class)
-class BacklightDialogViewModelTest : SysuiTestCase() {
-
-    private val keyboardRepository = FakeKeyboardRepository()
-    private lateinit var underTest: BacklightDialogViewModel
-    @Mock private lateinit var accessibilityManagerWrapper: AccessibilityManagerWrapper
-    private val timeoutMillis = 3000L
-
-    @Before
-    fun setUp() {
-        MockitoAnnotations.initMocks(this)
-        whenever(accessibilityManagerWrapper.getRecommendedTimeoutMillis(any(), any()))
-            .thenReturn(timeoutMillis.toInt())
-        underTest =
-            BacklightDialogViewModel(
-                KeyboardBacklightInteractor(keyboardRepository),
-                accessibilityManagerWrapper
-            )
-        keyboardRepository.setIsAnyKeyboardConnected(true)
-    }
-
-    @Test
-    fun emitsViewModel_whenBacklightChanged() = runTest {
-        keyboardRepository.setBacklight(BacklightModel(1, 5))
-
-        assertThat(underTest.dialogContent.first()).isEqualTo(BacklightDialogContentViewModel(1, 5))
-    }
-
-    @Test
-    fun emitsNull_afterTimeout() = runTest {
-        val latest by collectLastValue(underTest.dialogContent)
-        keyboardRepository.setBacklight(BacklightModel(1, 5))
-
-        assertThat(latest).isEqualTo(BacklightDialogContentViewModel(1, 5))
-        advanceTimeBy(timeoutMillis + 1)
-        assertThat(latest).isNull()
-    }
-
-    @Test
-    fun emitsNull_after5secDelay_fromLastBacklightChange() = runTest {
-        val latest by collectLastValue(underTest.dialogContent)
-        keyboardRepository.setIsAnyKeyboardConnected(true)
-
-        keyboardRepository.setBacklight(BacklightModel(1, 5))
-        assertThat(latest).isEqualTo(BacklightDialogContentViewModel(1, 5))
-
-        advanceTimeBy(timeoutMillis * 2 / 3)
-        // timeout yet to pass, no new emission
-        keyboardRepository.setBacklight(BacklightModel(2, 5))
-        assertThat(latest).isEqualTo(BacklightDialogContentViewModel(2, 5))
-
-        advanceTimeBy(timeoutMillis * 2 / 3)
-        // timeout refreshed because of last `setBacklight`, still content present
-        assertThat(latest).isEqualTo(BacklightDialogContentViewModel(2, 5))
-
-        advanceTimeBy(timeoutMillis * 2 / 3)
-        // finally timeout reached and null emitted
-        assertThat(latest).isNull()
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
index fd6e457..3bcefcf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt
@@ -716,6 +716,48 @@
     }
 
     @Test
+    fun testOnNotificationAdded_emptyMetadata_usesNotificationTitle() {
+        // When the app sets the metadata title fields to empty strings, but does include a
+        // non-blank notification title
+        val mockPackageManager = mock(PackageManager::class.java)
+        context.setMockPackageManager(mockPackageManager)
+        whenever(mockPackageManager.getApplicationLabel(any())).thenReturn(APP_NAME)
+        whenever(mediaFlags.isMediaTitleRequired(any(), any())).thenReturn(true)
+        whenever(controller.metadata)
+            .thenReturn(
+                metadataBuilder
+                    .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_EMPTY_TITLE)
+                    .putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE, SESSION_EMPTY_TITLE)
+                    .build()
+            )
+        mediaNotification =
+            SbnBuilder().run {
+                setPkg(PACKAGE_NAME)
+                modifyNotification(context).also {
+                    it.setSmallIcon(android.R.drawable.ic_media_pause)
+                    it.setContentTitle(SESSION_TITLE)
+                    it.setStyle(MediaStyle().apply { setMediaSession(session.sessionToken) })
+                }
+                build()
+            }
+        mediaDataManager.onNotificationAdded(KEY, mediaNotification)
+
+        // Then the media control is added using the notification's title
+        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
+        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
+        verify(listener)
+            .onMediaDataLoaded(
+                eq(KEY),
+                eq(null),
+                capture(mediaDataCaptor),
+                eq(true),
+                eq(0),
+                eq(false)
+            )
+        assertThat(mediaDataCaptor.value.song).isEqualTo(SESSION_TITLE)
+    }
+
+    @Test
     fun testOnNotificationRemoved_emptyTitle_notConverted() {
         // GIVEN that the manager has a notification with a resume action and empty title.
         addNotificationAndLoad()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
index ba6f536..f030a03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/MediaControlPanelTest.kt
@@ -230,7 +230,6 @@
         FakeFeatureFlags().apply {
             this.set(Flags.UMO_SURFACE_RIPPLE, false)
             this.set(Flags.UMO_TURBULENCE_NOISE, false)
-            this.set(Flags.MEDIA_FALSING_PENALTY, true)
             this.set(Flags.MEDIA_EXPLICIT_INDICATOR, true)
             this.set(Flags.MEDIA_RECOMMENDATION_CARD_UPDATE, false)
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 3d55c51..6720dae 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -321,4 +321,30 @@
         assertThat(mController.shouldUseHorizontalLayout()).isFalse();
         verify(mHorizontalLayoutListener).run();
     }
+
+    @Test
+    public void changeTiles_callbackRemovedOnOldOnes() {
+        // Start with one tile
+        assertThat(mController.mRecords.size()).isEqualTo(1);
+        QSPanelControllerBase.TileRecord record = mController.mRecords.get(0);
+
+        assertThat(record.tile).isEqualTo(mQSTile);
+
+        // Change to a different tile
+        when(mQSHost.getTiles()).thenReturn(List.of(mOtherTile));
+        mController.setTiles();
+
+        verify(mQSTile).removeCallback(record.callback);
+        verify(mOtherTile, never()).removeCallback(any());
+        verify(mOtherTile, never()).removeCallbacks();
+    }
+
+    @Test
+    public void onViewDetached_removesJustTheAssociatedCallback() {
+        QSPanelControllerBase.TileRecord record = mController.mRecords.get(0);
+
+        mController.onViewDetached();
+        verify(mQSTile).removeCallback(record.callback);
+        verify(mQSTile, never()).removeCallbacks();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
index 93cebe2..a60dad4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -27,6 +27,8 @@
 import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.qs.QSTileView
+import com.android.systemui.qs.QSPanelControllerBase.TileRecord
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.tileimpl.QSIconViewImpl
 import com.android.systemui.qs.tileimpl.QSTileViewImpl
@@ -192,6 +194,18 @@
         verify(accessibilityInfo, never()).addAction(actionCollapse)
     }
 
+    @Test
+    fun addTile_callbackAdded() {
+        val tile = mock(QSTile::class.java)
+        val tileView = mock(QSTileView::class.java)
+
+        val record = TileRecord(tile, tileView)
+
+        qsPanel.addTile(record)
+
+        verify(tile).addCallback(record.callback)
+    }
+
     private infix fun View.isLeftOf(other: View): Boolean {
         val rect = Rect()
         getBoundsOnScreen(rect)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
index 7ecb4dc..426ff67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/CurrentTilesInteractorImplTest.kt
@@ -589,6 +589,26 @@
                 .isTrue()
         }
 
+    @Test
+    fun retainedTiles_callbackNotRemoved() =
+        testScope.runTest(USER_INFO_0) {
+            val tiles by collectLastValue(underTest.currentTiles)
+            tileSpecRepository.setTiles(USER_INFO_0.id, listOf(TileSpec.create("a")))
+
+            val tileA = tiles!![0].tile
+            val callback = mock<QSTile.Callback>()
+            tileA.addCallback(callback)
+
+            tileSpecRepository.setTiles(
+                USER_INFO_0.id,
+                listOf(TileSpec.create("a"), CUSTOM_TILE_SPEC)
+            )
+            val newTileA = tiles!![0].tile
+            assertThat(tileA).isSameInstanceAs(newTileA)
+
+            assertThat((tileA as FakeQSTile).callbacks).containsExactly(callback)
+        }
+
     private fun QSTile.State.fillIn(state: Int, label: CharSequence, secondaryLabel: CharSequence) {
         this.state = state
         this.label = label
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
index e509696..013c925 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/domain/interactor/FakeQSTile.kt
@@ -29,6 +29,7 @@
     private var tileSpec: String? = null
     var destroyed = false
     private val state = QSTile.State()
+    val callbacks = mutableListOf<QSTile.Callback>()
 
     override fun getTileSpec(): String? {
         return tileSpec
@@ -45,11 +46,17 @@
 
     override fun refreshState() {}
 
-    override fun addCallback(callback: QSTile.Callback?) {}
+    override fun addCallback(callback: QSTile.Callback) {
+        callbacks.add(callback)
+    }
 
-    override fun removeCallback(callback: QSTile.Callback?) {}
+    override fun removeCallback(callback: QSTile.Callback) {
+        callbacks.remove(callback)
+    }
 
-    override fun removeCallbacks() {}
+    override fun removeCallbacks() {
+        callbacks.clear()
+    }
 
     override fun createTileView(context: Context?): QSIconView? {
         return null
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
index 7c30843b..3def6ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/RecordingServiceTest.java
@@ -46,6 +46,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
@@ -73,6 +75,8 @@
     private Handler mHandler;
     @Mock
     private UserContextProvider mUserContextTracker;
+    @Captor
+    private ArgumentCaptor<Runnable> mRunnableCaptor;
     private KeyguardDismissUtil mKeyguardDismissUtil = new KeyguardDismissUtil() {
         public void executeWhenUnlocked(ActivityStarter.OnDismissAction action,
                 boolean requiresShadeOpen) {
@@ -209,4 +213,19 @@
 
         verify(mScreenMediaRecorder).release();
     }
+
+    @Test
+    public void testOnErrorSaving() throws IOException {
+        // When the screen recording does not save properly
+        doThrow(new IllegalStateException("fail")).when(mScreenMediaRecorder).save();
+
+        Intent startIntent = RecordingService.getStopIntent(mContext);
+        mRecordingService.onStartCommand(startIntent, 0, 0);
+        verify(mExecutor).execute(mRunnableCaptor.capture());
+        mRunnableCaptor.getValue().run();
+
+        // Then the state is set to not recording and we cancel the notification
+        verify(mController).updateState(false);
+        verify(mNotificationManager).cancelAsUser(any(), anyInt(), any());
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
index 13a2baa..487d26d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
@@ -114,6 +114,54 @@
     }
 
     @Test
+    public void testShouldHeadsUpBecomePinned_hasFSI_notUnpinned_true() {
+        // Set up NotifEntry with FSI
+        NotificationEntry notifEntry = new NotificationEntryBuilder()
+                .setSbn(createNewNotification(/* id= */ 0))
+                .build();
+        notifEntry.getSbn().getNotification().fullScreenIntent = PendingIntent.getActivity(
+                getContext(), 0, new Intent(getContext(), this.getClass()),
+                PendingIntent.FLAG_MUTABLE_UNAUDITED);
+
+        // Add notifEntry to ANM mAlertEntries map and make it NOT unpinned
+        mHeadsUpManager.showNotification(notifEntry);
+        HeadsUpManager.HeadsUpEntry headsUpEntry =
+                mHeadsUpManager.getHeadsUpEntry(notifEntry.getKey());
+        headsUpEntry.wasUnpinned = false;
+
+        assertTrue(mHeadsUpManager.shouldHeadsUpBecomePinned(notifEntry));
+    }
+
+    @Test
+    public void testShouldHeadsUpBecomePinned_wasUnpinned_false() {
+        // Set up NotifEntry with FSI
+        NotificationEntry notifEntry = new NotificationEntryBuilder()
+                .setSbn(createNewNotification(/* id= */ 0))
+                .build();
+        notifEntry.getSbn().getNotification().fullScreenIntent = PendingIntent.getActivity(
+                getContext(), 0, new Intent(getContext(), this.getClass()),
+                PendingIntent.FLAG_MUTABLE_UNAUDITED);
+
+        // Add notifEntry to ANM mAlertEntries map and make it unpinned
+        mHeadsUpManager.showNotification(notifEntry);
+        HeadsUpManager.HeadsUpEntry headsUpEntry =
+                mHeadsUpManager.getHeadsUpEntry(notifEntry.getKey());
+        headsUpEntry.wasUnpinned = true;
+
+        assertFalse(mHeadsUpManager.shouldHeadsUpBecomePinned(notifEntry));
+    }
+
+    @Test
+    public void testShouldHeadsUpBecomePinned_noFSI_false() {
+        // Set up NotifEntry with no FSI
+        NotificationEntry notifEntry = new NotificationEntryBuilder()
+                .setSbn(createNewNotification(/* id= */ 0))
+                .build();
+
+        assertFalse(mHeadsUpManager.shouldHeadsUpBecomePinned(notifEntry));
+    }
+
+    @Test
     public void testShowNotification_autoDismissesWithAccessibilityTimeout() {
         doReturn(TEST_A11Y_AUTO_DISMISS_TIME).when(mAccessibilityMgr)
                 .getRecommendedTimeoutMillis(anyInt(), anyInt());
diff --git a/services/autofill/java/com/android/server/autofill/FillResponseEventLogger.java b/services/autofill/java/com/android/server/autofill/FillResponseEventLogger.java
index fc5fb1a..a69e33a 100644
--- a/services/autofill/java/com/android/server/autofill/FillResponseEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/FillResponseEventLogger.java
@@ -26,6 +26,9 @@
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__AUTHENTICATION_TYPE__AUTHENTICATION_TYPE_UNKNOWN;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__AUTHENTICATION_TYPE__DATASET_AUTHENTICATION;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__AUTHENTICATION_TYPE__FULL_AUTHENTICATION;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_AUTOFILL_PROVIDER;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_PCC;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_UNKONWN;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__DIALOG;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__MENU;
@@ -40,6 +43,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.os.SystemClock;
 import android.service.autofill.Dataset;
 import android.util.Slog;
 import android.view.autofill.AutofillId;
@@ -57,6 +61,9 @@
 public final class FillResponseEventLogger {
   private static final String TAG = "FillResponseEventLogger";
 
+  private static final long UNINITIALIZED_TIMESTAMP = -1;
+  private long startResponseProcessingTimestamp = UNINITIALIZED_TIMESTAMP;
+
   /**
    * Reasons why presentation was not shown. These are wrappers around
    * {@link com.android.os.AtomsProto.AutofillFillRequestReported.RequestTriggerReason}.
@@ -114,6 +121,20 @@
   public @interface AuthenticationResult {
   }
 
+
+  /**
+   * Reasons why presentation was not shown. These are wrappers around
+   * {@link com.android.os.AtomsProto.AutofillFillResponseReported.DetectionPreference}.
+   */
+  @IntDef(prefix = {"DETECTION_PREFER"}, value = {
+      DETECTION_PREFER_UNKNOWN,
+      DETECTION_PREFER_AUTOFILL_PROVIDER,
+      DETECTION_PREFER_PCC
+  })
+  @Retention(RetentionPolicy.SOURCE)
+  public @interface DetectionPreference {
+  }
+
   public static final int DISPLAY_PRESENTATION_TYPE_UNKNOWN =
       AUTOFILL_FILL_RESPONSE_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE;
   public static final int DISPLAY_PRESENTATION_TYPE_MENU =
@@ -148,6 +169,15 @@
   public static final int RESPONSE_STATUS_UNKNOWN =
       AUTOFILL_FILL_RESPONSE_REPORTED__RESPONSE_STATUS__RESPONSE_STATUS_UNKNOWN;
 
+  // Values for AutofillFillResponseReported.detection_preference
+  public static final int DETECTION_PREFER_UNKNOWN =
+          AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_UNKONWN;
+  public static final int DETECTION_PREFER_AUTOFILL_PROVIDER =
+          AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_AUTOFILL_PROVIDER;
+  public static final int DETECTION_PREFER_PCC =
+          AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_PCC;
+
+
   // Log a magic number when FillRequest failed or timeout to differentiate with FillRequest
   // succeeded.
   public static final int AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT = -1;
@@ -221,15 +251,21 @@
 
   public void maybeSetAvailableCount(int val) {
     mEventInternal.ifPresent(event -> {
+      event.mAvailableCount = val;
+    });
+  }
+
+  public void maybeSetTotalDatasetsProvided(int val) {
+    mEventInternal.ifPresent(event -> {
       // Don't reset if it's already populated.
       // This is just a technical limitation of not having complicated logic.
       // Autofill Provider may return some datasets which are applicable to data types.
       // In such a case, we set available count to the number of datasets provided.
       // However, it's possible that those data types aren't detected by PCC, so in effect, there
       // are 0 datasets. In the codebase, we treat it as null response, which may call this again
-      // to set 0. But we don't want to overwrite this value.
-      if (event.mAvailableCount == 0) {
-        event.mAvailableCount = val;
+      // to set 0. But we don't want to overwrite already set value.
+      if (event.mTotalDatasetsProvided == -1) {
+        event.mTotalDatasetsProvided = val;
       }
     });
   }
@@ -321,12 +357,20 @@
     });
   }
 
+  public void startResponseProcessingTime() {
+    startResponseProcessingTimestamp = SystemClock.elapsedRealtime();
+  }
+
   /**
    * Set latency_response_processing_millis as long as mEventInternal presents.
    */
-  public void maybeSetLatencyResponseProcessingMillis(int val) {
+  public void maybeSetLatencyResponseProcessingMillis() {
     mEventInternal.ifPresent(event -> {
-      event.mLatencyResponseProcessingMillis = val;
+      if (startResponseProcessingTimestamp == UNINITIALIZED_TIMESTAMP && sVerbose) {
+        Slog.v(TAG, "uninitialized startResponseProcessingTimestamp");
+      }
+      event.mLatencyResponseProcessingMillis
+              = SystemClock.elapsedRealtime() - startResponseProcessingTimestamp;
     });
   }
 
@@ -351,11 +395,13 @@
   /**
    * Set available_pcc_count.
    */
-  public void maybeSetAvailableDatasetsPccCount(@Nullable List<Dataset> datasetList) {
+  public void maybeSetDatasetsCountAfterPotentialPccFiltering(@Nullable List<Dataset> datasetList) {
     mEventInternal.ifPresent(event -> {
       int pccOnlyCount = 0;
       int pccCount = 0;
+      int totalCount = 0;
       if (datasetList != null) {
+        totalCount = datasetList.size();
         for (int i = 0; i < datasetList.size(); i++) {
           Dataset dataset = datasetList.get(i);
           if (dataset != null) {
@@ -371,9 +417,18 @@
       }
       event.mAvailablePccOnlyCount = pccOnlyCount;
       event.mAvailablePccCount = pccCount;
+      event.mAvailableCount = totalCount;
     });
   }
 
+  /**
+   * Set detection_pref
+   */
+  public void maybeSetDetectionPreference(@DetectionPreference int detectionPreference) {
+    mEventInternal.ifPresent(event -> {
+      event.mDetectionPref = detectionPreference;
+    });
+  }
 
   /**
    * Log an AUTOFILL_FILL_RESPONSE_REPORTED event.
@@ -402,7 +457,9 @@
           + " mResponseStatus=" + event.mResponseStatus
           + " mLatencyResponseProcessingMillis=" + event.mLatencyResponseProcessingMillis
           + " mAvailablePccCount=" + event.mAvailablePccCount
-          + " mAvailablePccOnlyCount=" + event.mAvailablePccOnlyCount);
+          + " mAvailablePccOnlyCount=" + event.mAvailablePccOnlyCount
+          + " mTotalDatasetsProvided=" + event.mTotalDatasetsProvided
+          + " mDetectionPref=" + event.mDetectionPref);
     }
     FrameworkStatsLog.write(
         AUTOFILL_FILL_RESPONSE_REPORTED,
@@ -421,7 +478,9 @@
         event.mResponseStatus,
         event.mLatencyResponseProcessingMillis,
         event.mAvailablePccCount,
-        event.mAvailablePccOnlyCount);
+        event.mAvailablePccOnlyCount,
+        event.mTotalDatasetsProvided,
+        event.mDetectionPref);
     mEventInternal = Optional.empty();
   }
 
@@ -431,16 +490,19 @@
     int mDisplayPresentationType = DISPLAY_PRESENTATION_TYPE_UNKNOWN;
     int mAvailableCount = 0;
     int mSaveUiTriggerIds = -1;
-    int mLatencyFillResponseReceivedMillis = 0;
+    int mLatencyFillResponseReceivedMillis = (int) UNINITIALIZED_TIMESTAMP;
     int mAuthenticationType = AUTHENTICATION_TYPE_UNKNOWN;
     int mAuthenticationResult = AUTHENTICATION_RESULT_UNKNOWN;
     int mAuthenticationFailureReason = -1;
-    int mLatencyAuthenticationUiDisplayMillis = 0;
-    int mLatencyDatasetDisplayMillis = 0;
+    int mLatencyAuthenticationUiDisplayMillis = (int) UNINITIALIZED_TIMESTAMP;
+    int mLatencyDatasetDisplayMillis = (int) UNINITIALIZED_TIMESTAMP;
     int mResponseStatus = RESPONSE_STATUS_UNKNOWN;
-    int mLatencyResponseProcessingMillis = 0;
-    int mAvailablePccCount;
-    int mAvailablePccOnlyCount;
+    long mLatencyResponseProcessingMillis = UNINITIALIZED_TIMESTAMP;
+    int mAvailablePccCount = -1;
+    int mAvailablePccOnlyCount = -1;
+    int mTotalDatasetsProvided = -1;
+    @DetectionPreference
+    int mDetectionPref = DETECTION_PREFER_UNKNOWN;
 
     FillResponseEventInternal() {
     }
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index b2f9a93..11b45db 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -16,8 +16,6 @@
 
 package com.android.server.autofill;
 
-import static android.service.autofill.Dataset.PICK_REASON_PCC_DETECTION_ONLY;
-import static android.service.autofill.Dataset.PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER;
 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_DIALOG;
 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_INLINE;
 import static android.service.autofill.FillEventHistory.Event.UI_TYPE_MENU;
@@ -27,6 +25,9 @@
 import static android.view.autofill.AutofillManager.COMMIT_REASON_VIEW_CLICKED;
 import static android.view.autofill.AutofillManager.COMMIT_REASON_VIEW_COMMITTED;
 
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_AUTOFILL_PROVIDER;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_PCC;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_UNKONWN;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__AUTHENTICATION_RESULT__AUTHENTICATION_FAILURE;
 import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__AUTHENTICATION_RESULT__AUTHENTICATION_RESULT_UNKNOWN;
@@ -140,6 +141,19 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface DatasetPickedReason {}
 
+    /**
+     * The type of detection that was preferred. These are wrappers around
+     * {@link com.android.os.AtomsProto.AutofillPresentationEventReported.DetectionPreference}.
+     */
+    @IntDef(prefix = {"DETECTION_PREFER"}, value = {
+            DETECTION_PREFER_UNKNOWN,
+            DETECTION_PREFER_AUTOFILL_PROVIDER,
+            DETECTION_PREFER_PCC
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface DetectionPreference {
+    }
+
     public static final int NOT_SHOWN_REASON_ANY_SHOWN =
             AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN;
     public static final int NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED =
@@ -187,6 +201,15 @@
             AUTOFILL_PRESENTATION_EVENT_REPORTED__SELECTED_DATASET_PICKED_REASON__PICK_REASON_PCC_DETECTION_ONLY;
     public static final int PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER =
             AUTOFILL_PRESENTATION_EVENT_REPORTED__SELECTED_DATASET_PICKED_REASON__PICK_REASON_PCC_DETECTION_PREFERRED_WITH_PROVIDER;
+
+
+    // Values for AutofillFillResponseReported.detection_preference
+    public static final int DETECTION_PREFER_UNKNOWN =
+            AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_UNKONWN;
+    public static final int DETECTION_PREFER_AUTOFILL_PROVIDER =
+            AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_AUTOFILL_PROVIDER;
+    public static final int DETECTION_PREFER_PCC =
+            AUTOFILL_FILL_RESPONSE_REPORTED__DETECTION_PREFERENCE__DETECTION_PREFER_PCC;
     private final int mSessionId;
     private Optional<PresentationStatsEventInternal> mEventInternal;
 
@@ -463,6 +486,15 @@
         });
     }
 
+    /**
+     * Set detection_pref
+     */
+    public void maybeSetDetectionPreference(@DetectionPreference int detectionPreference) {
+        mEventInternal.ifPresent(event -> {
+            event.mDetectionPreference = detectionPreference;
+        });
+    }
+
     private int convertDatasetPickReason(@Dataset.DatasetEligibleReason int val) {
         switch (val) {
             case 0:
@@ -514,7 +546,8 @@
                     + " mLatencyDatasetDisplayMillis=" + event.mLatencyDatasetDisplayMillis
                     + " mAvailablePccCount=" + event.mAvailablePccCount
                     + " mAvailablePccOnlyCount=" + event.mAvailablePccOnlyCount
-                    + " mSelectedDatasetPickedReason=" + event.mSelectedDatasetPickedReason);
+                    + " mSelectedDatasetPickedReason=" + event.mSelectedDatasetPickedReason
+                    + " mDetectionPreference=" + event.mDetectionPreference);
         }
 
         // TODO(b/234185326): Distinguish empty responses from other no presentation reasons.
@@ -550,7 +583,8 @@
                 event.mLatencyDatasetDisplayMillis,
                 event.mAvailablePccCount,
                 event.mAvailablePccOnlyCount,
-                event.mSelectedDatasetPickedReason);
+                event.mSelectedDatasetPickedReason,
+                event.mDetectionPreference);
         mEventInternal = Optional.empty();
     }
 
@@ -582,6 +616,7 @@
         int mAvailablePccCount = -1;
         int mAvailablePccOnlyCount = -1;
         @DatasetPickedReason int mSelectedDatasetPickedReason = PICK_REASON_UNKNOWN;
+        @DetectionPreference int mDetectionPreference = DETECTION_PREFER_UNKNOWN;
 
         PresentationStatsEventInternal() {}
     }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 5d6eab7..0a8f474 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -52,6 +52,9 @@
 import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_PRE_TRIGGER;
 import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_SERVED_FROM_CACHED_RESPONSE;
 import static com.android.server.autofill.FillResponseEventLogger.AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT;
+import static com.android.server.autofill.FillResponseEventLogger.DETECTION_PREFER_AUTOFILL_PROVIDER;
+import static com.android.server.autofill.FillResponseEventLogger.DETECTION_PREFER_UNKNOWN;
+import static com.android.server.autofill.FillResponseEventLogger.DETECTION_PREFER_PCC;
 import static com.android.server.autofill.FillResponseEventLogger.HAVE_SAVE_TRIGGER_ID;
 import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_FAILURE;
 import static com.android.server.autofill.FillResponseEventLogger.RESPONSE_STATUS_SESSION_DESTROYED;
@@ -1460,6 +1463,7 @@
         mFillResponseEventLogger.maybeSetRequestId(requestId);
         mFillResponseEventLogger.maybeSetAppPackageUid(uid);
         mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SUCCESS);
+        mFillResponseEventLogger.startResponseProcessingTime();
         // Time passed since session was created
         final long fillRequestReceivedRelativeTimestamp =
             SystemClock.elapsedRealtime() - mLatencyBaseTime;
@@ -1467,6 +1471,7 @@
             (int) (fillRequestReceivedRelativeTimestamp));
         mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis(
             (int) (fillRequestReceivedRelativeTimestamp));
+        mFillResponseEventLogger.maybeSetDetectionPreference(getDetectionPreferenceForLogging());
 
         synchronized (mLock) {
             if (mDestroyed) {
@@ -1485,6 +1490,7 @@
                 Slog.w(TAG, "onFillRequestSuccess(): no request log for id " + requestId);
             }
             if (response == null) {
+                mFillResponseEventLogger.maybeSetTotalDatasetsProvided(0);
                 if (requestLog != null) {
                     requestLog.addTaggedData(MetricsEvent.FIELD_AUTOFILL_NUM_DATASETS, -1);
                 }
@@ -1584,13 +1590,16 @@
             }
         }
 
-        mFillResponseEventLogger.maybeSetAvailableCount(
-                datasetList == null ? 0 : datasetList.size());
+        int datasetCount = (datasetList == null) ? 0 : datasetList.size();
+        mFillResponseEventLogger.maybeSetTotalDatasetsProvided(datasetCount);
+        // It's possible that this maybe overwritten later on after PCC filtering.
+        mFillResponseEventLogger.maybeSetAvailableCount(datasetCount);
 
         // TODO(b/266379948): Ideally wait for PCC request to finish for a while more
         // (say 100ms) before proceeding further on.
 
         processResponseLockedForPcc(response, response.getClientState(), requestFlags);
+        mFillResponseEventLogger.maybeSetLatencyResponseProcessingMillis();
     }
 
 
@@ -1993,7 +2002,7 @@
                                 null,
                                 dataset.getId(),
                                 dataset.getAuthentication());
-                dataset.setEligibleReasonReason(pickReason);
+                newDataset.setEligibleReasonReason(pickReason);
                 eligibleDatasets.add(newDataset);
                 Set<Dataset> newDatasets;
                 for (AutofillId autofillId : datasetAutofillIds) {
@@ -2036,7 +2045,10 @@
         mFillResponseEventLogger.maybeSetRequestId(requestId);
         mFillResponseEventLogger.maybeSetAppPackageUid(uid);
         mFillResponseEventLogger.maybeSetAvailableCount(
-            AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT);
+                AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT);
+        mFillResponseEventLogger.maybeSetTotalDatasetsProvided(
+                AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT);
+        mFillResponseEventLogger.maybeSetDetectionPreference(getDetectionPreferenceForLogging());
         final long fillRequestReceivedRelativeTimestamp =
             SystemClock.elapsedRealtime() - mLatencyBaseTime;
         mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis(
@@ -3887,8 +3899,7 @@
                 // View is triggering autofill.
                 mCurrentViewId = viewState.id;
                 viewState.update(value, virtualBounds, flags);
-                mPresentationStatsEventLogger.startNewEvent();
-                mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
+                startNewEventForPresentationStatsEventLogger();
                 mPresentationStatsEventLogger.maybeSetIsNewRequest(true);
                 if (!isRequestSupportFillDialog(flags)) {
                     mSessionFlags.mFillDialogDisabled = true;
@@ -4021,9 +4032,7 @@
                 }
                 // If previous request was FillDialog request, a logger event was already started
                 if (!wasPreviouslyFillDialog) {
-                    mPresentationStatsEventLogger.startNewEvent();
-                    mPresentationStatsEventLogger.maybeSetAutofillServiceUid(
-                            getAutofillServiceUid());
+                    startNewEventForPresentationStatsEventLogger();
                 }
                 if (requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags)) {
                     // If a new request was issued even if previously it was fill dialog request,
@@ -4032,9 +4041,7 @@
                     // lock guarded, we should be safe.
                     if (wasPreviouslyFillDialog) {
                         mPresentationStatsEventLogger.logAndEndEvent();
-                        mPresentationStatsEventLogger.startNewEvent();
-                        mPresentationStatsEventLogger.maybeSetAutofillServiceUid(
-                                getAutofillServiceUid());
+                        startNewEventForPresentationStatsEventLogger();
                     }
                     return;
                 }
@@ -4966,7 +4973,7 @@
         List<Dataset> datasetList = newResponse.getDatasets();
 
         mPresentationStatsEventLogger.maybeSetAvailableCount(datasetList, mCurrentViewId);
-        mFillResponseEventLogger.maybeSetAvailableDatasetsPccCount(datasetList);
+        mFillResponseEventLogger.maybeSetDatasetsCountAfterPotentialPccFiltering(datasetList);
 
         setViewStatesLocked(newResponse, ViewState.STATE_FILLABLE, false);
         updateFillDialogTriggerIdsLocked();
@@ -5154,6 +5161,25 @@
         };
     }
 
+    private int getDetectionPreferenceForLogging() {
+        if (mService.isPccClassificationEnabled()) {
+            if (mService.getMaster().preferProviderOverPcc()) {
+                return DETECTION_PREFER_AUTOFILL_PROVIDER;
+            }
+            return DETECTION_PREFER_PCC;
+        }
+        return DETECTION_PREFER_UNKNOWN;
+    }
+
+    private void startNewEventForPresentationStatsEventLogger() {
+        synchronized (mLock) {
+            mPresentationStatsEventLogger.startNewEvent();
+            mPresentationStatsEventLogger.maybeSetDetectionPreference(
+                    getDetectionPreferenceForLogging());
+            mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
+        }
+    }
+
     private void startAuthentication(int authenticationId, IntentSender intent,
             Intent fillInIntent, boolean authenticateInline) {
         try {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ca4a253..3d02c96 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17236,12 +17236,6 @@
         }
     }
 
-    void onProcessFreezableChangedLocked(ProcessRecord app) {
-        if (mEnableModernQueue) {
-            mBroadcastQueues[0].onProcessFreezableChangedLocked(app);
-        }
-    }
-
     @VisibleForTesting
     public final class LocalService extends ActivityManagerInternal
             implements ActivityManagerLocal {
@@ -18347,16 +18341,17 @@
         @Override
         public boolean hasRunningForegroundService(int uid, int foregroundServicetype) {
             synchronized (ActivityManagerService.this) {
-                return mProcessList.searchEachLruProcessesLOSP(true, app -> {
-                    if (app.uid != uid) {
-                        return null;
-                    }
-
+                final UidRecord uidRec = mProcessList.mActiveUids.get(uid);
+                if (uidRec == null) {
+                    return false;
+                }
+                for (int i = uidRec.getNumOfProcs() - 1; i >= 0; i--) {
+                    final ProcessRecord app = uidRec.getProcessRecordByIndex(i);
                     if ((app.mServices.containsAnyForegroundServiceTypes(foregroundServicetype))) {
-                        return Boolean.TRUE;
+                        return true;
                     }
-                    return null;
-                }) != null;
+                }
+                return false;
             }
         }
 
@@ -19043,8 +19038,11 @@
             long delayedDurationMs) {
         Objects.requireNonNull(targetPackage);
         Preconditions.checkArgumentNonnegative(delayedDurationMs);
-        Preconditions.checkState(mEnableModernQueue, "Not valid in legacy queue");
         enforceCallingPermission(permission.DUMP, "forceDelayBroadcastDelivery()");
+        // Ignore request if modern queue is not enabled
+        if (!mEnableModernQueue) {
+            return;
+        }
 
         for (BroadcastQueue queue : mBroadcastQueues) {
             queue.forceDelayBroadcastDelivery(targetPackage, delayedDurationMs);
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 142a5dc..3ac2b2b 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -201,7 +201,7 @@
     private boolean mLastDeferredStates;
 
     private boolean mUidForeground;
-    private boolean mProcessFreezable;
+    private boolean mUidCached;
     private boolean mProcessInstrumented;
     private boolean mProcessPersistent;
 
@@ -411,7 +411,7 @@
      */
     @CheckResult
     public boolean setProcessAndUidState(@Nullable ProcessRecord app, boolean uidForeground,
-            boolean processFreezable) {
+            boolean uidCached) {
         this.app = app;
 
         // Since we may have just changed our PID, invalidate cached strings
@@ -420,13 +420,13 @@
 
         boolean didSomething = false;
         if (app != null) {
+            didSomething |= setUidCached(uidCached);
             didSomething |= setUidForeground(uidForeground);
-            didSomething |= setProcessFreezable(processFreezable);
             didSomething |= setProcessInstrumented(app.getActiveInstrumentation() != null);
             didSomething |= setProcessPersistent(app.isPersistent());
         } else {
+            didSomething |= setUidCached(uidCached);
             didSomething |= setUidForeground(false);
-            didSomething |= setProcessFreezable(false);
             didSomething |= setProcessInstrumented(false);
             didSomething |= setProcessPersistent(false);
         }
@@ -450,13 +450,13 @@
     }
 
     /**
-     * Update if this process is in the "freezable" state, typically signaling that
+     * Update if this process is in the "cached" state, typically signaling that
      * broadcast dispatch should be paused or delayed.
      */
     @CheckResult
-    private boolean setProcessFreezable(boolean freezable) {
-        if (mProcessFreezable != freezable) {
-            mProcessFreezable = freezable;
+    private boolean setUidCached(boolean uidCached) {
+        if (mUidCached != uidCached) {
+            mUidCached = uidCached;
             invalidateRunnableAt();
             return true;
         } else {
@@ -1115,7 +1115,7 @@
             } else if (mCountManifest > 0) {
                 mRunnableAt = runnableAt;
                 mRunnableAtReason = REASON_CONTAINS_MANIFEST;
-            } else if (mProcessFreezable) {
+            } else if (mUidCached) {
                 if (r.deferUntilActive) {
                     // All enqueued broadcasts are deferrable, defer
                     if (mCountDeferred == mCountEnqueued) {
@@ -1185,7 +1185,7 @@
         // When all we have pending is deferred broadcasts, and we're cached,
         // then we want everything to be marked deferred
         final boolean wantDeferredStates = (mCountDeferred > 0)
-                && (mCountDeferred == mCountEnqueued) && mProcessFreezable;
+                && (mCountDeferred == mCountEnqueued) && mUidCached;
 
         if (mLastDeferredStates != wantDeferredStates) {
             mLastDeferredStates = wantDeferredStates;
@@ -1372,9 +1372,9 @@
         if (mUidForeground) {
             sb.append("FG");
         }
-        if (mProcessFreezable) {
+        if (mUidCached) {
             if (sb.length() > 0) sb.append("|");
-            sb.append("FRZN");
+            sb.append("CACHED");
         }
         if (mProcessInstrumented) {
             if (sb.length() > 0) sb.append("|");
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 93bde6f..8e76e5b 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -173,13 +173,6 @@
     public abstract void onApplicationCleanupLocked(@NonNull ProcessRecord app);
 
     /**
-     * Signal from OS internals that the given process is in a freezable state and will be
-     * freezed soon after.
-     */
-    @GuardedBy("mService")
-    public abstract void onProcessFreezableChangedLocked(@NonNull ProcessRecord app);
-
-    /**
      * Signal from OS internals that the given package (or some subset of that
      * package) has been disabled or uninstalled, and that any pending
      * broadcasts should be cleaned up.
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index ce635f1..e389821 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET;
 import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_START_RECEIVER;
+import static android.os.PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_APP_FREEZING_DELAYED;
 import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
 import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
 import static android.text.TextUtils.formatSimple;
@@ -384,6 +385,16 @@
         maybeReportBroadcastDispatchedEventLocked(r, r.curReceiver.applicationInfo.uid);
         r.intent.setComponent(r.curComponent);
 
+        // See if we need to delay the freezer based on BroadcastOptions
+        if (r.options != null
+                && r.options.getTemporaryAppAllowlistDuration() > 0
+                && r.options.getTemporaryAppAllowlistType()
+                    == TEMPORARY_ALLOW_LIST_TYPE_APP_FREEZING_DELAYED) {
+            mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(app,
+                    CachedAppOptimizer.UNFREEZE_REASON_START_RECEIVER,
+                    r.options.getTemporaryAppAllowlistDuration());
+        }
+
         boolean started = false;
         try {
             if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST,
@@ -449,10 +460,6 @@
         skipCurrentOrPendingReceiverLocked(app);
     }
 
-    public void onProcessFreezableChangedLocked(ProcessRecord app) {
-        // Not supported; ignore
-    }
-
     public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
         boolean didSomething = false;
         final BroadcastRecord br = mPendingBroadcast;
@@ -934,8 +941,13 @@
             Slog.v(TAG, "Broadcast temp allowlist uid=" + uid + " duration=" + duration
                     + " type=" + type + " : " + b.toString());
         }
-        mService.tempAllowlistUidLocked(uid, duration, reasonCode, b.toString(), type,
-                r.callingUid);
+
+        // Only add to temp allowlist if it's not the APP_FREEZING_DELAYED type. That will be
+        // handled when the broadcast is actually being scheduled on the app thread.
+        if (type != TEMPORARY_ALLOW_LIST_TYPE_APP_FREEZING_DELAYED) {
+            mService.tempAllowlistUidLocked(uid, duration, reasonCode, b.toString(), type,
+                    r.callingUid);
+        }
     }
 
     private void processNextBroadcast(boolean fromMsg) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 331efaa..d6e692c 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -223,6 +223,16 @@
     @GuardedBy("mService")
     private final SparseBooleanArray mUidForeground = new SparseBooleanArray();
 
+    /**
+     * Map from UID to its last known "cached" state.
+     * <p>
+     * We manually maintain this data structure since the lifecycle of
+     * {@link ProcessRecord} and {@link BroadcastProcessQueue} can be
+     * mismatched.
+     */
+    @GuardedBy("mService")
+    private final SparseBooleanArray mUidCached = new SparseBooleanArray();
+
     private final BroadcastConstants mConstants;
     private final BroadcastConstants mFgConstants;
     private final BroadcastConstants mBgConstants;
@@ -565,13 +575,6 @@
     }
 
     @Override
-    public void onProcessFreezableChangedLocked(@NonNull ProcessRecord app) {
-        synchronized (mService) {
-            refreshProcessQueueLocked(app);
-        }
-    }
-
-    @Override
     public int getPreferredSchedulingGroupLocked(@NonNull ProcessRecord app) {
         final BroadcastProcessQueue queue = getProcessQueue(app);
         if ((queue != null) && getRunningIndexOf(queue) >= 0) {
@@ -1302,6 +1305,7 @@
             };
             broadcastPredicate = BROADCAST_PREDICATE_ANY;
 
+            cleanupUserStateLocked(mUidCached, userId);
             cleanupUserStateLocked(mUidForeground, userId);
         }
         return forEachMatchingBroadcast(queuePredicate, broadcastPredicate,
@@ -1444,7 +1448,20 @@
                     refreshProcessQueuesLocked(uid);
                 }
             }
-        }, ActivityManager.UID_OBSERVER_PROCSTATE, ActivityManager.PROCESS_STATE_TOP, "android");
+
+            @Override
+            public void onUidCachedChanged(int uid, boolean cached) {
+                synchronized (mService) {
+                    if (cached) {
+                        mUidCached.put(uid, true);
+                    } else {
+                        mUidCached.delete(uid);
+                    }
+                    refreshProcessQueuesLocked(uid);
+                }
+            }
+        }, ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_CACHED,
+                ActivityManager.PROCESS_STATE_TOP, "android");
 
         // Kick off periodic health checks
         mLocalHandler.sendEmptyMessage(MSG_CHECK_HEALTH);
@@ -1633,9 +1650,10 @@
             // warm via this operation, we're going to immediately promote it to
             // be running, and any side effect of this operation will then apply
             // after it's finished and is returned to the runnable list.
-            final ProcessRecord app = mService.getProcessRecordLocked(queue.processName, queue.uid);
-            queue.setProcessAndUidState(app, mUidForeground.get(queue.uid, false),
-                    isProcessFreezable(app));
+            queue.setProcessAndUidState(
+                    mService.getProcessRecordLocked(queue.processName, queue.uid),
+                    mUidForeground.get(queue.uid, false),
+                    mUidCached.get(queue.uid, false));
         }
     }
 
@@ -1647,21 +1665,11 @@
     private void setQueueProcess(@NonNull BroadcastProcessQueue queue,
             @Nullable ProcessRecord app) {
         if (queue.setProcessAndUidState(app, mUidForeground.get(queue.uid, false),
-                isProcessFreezable(app))) {
+                mUidCached.get(queue.uid, false))) {
             updateRunnableList(queue);
         }
     }
 
-    @GuardedBy("mService")
-    private boolean isProcessFreezable(@Nullable ProcessRecord app) {
-        if (app == null) {
-            return false;
-        }
-        synchronized (mService.mProcLock) {
-            return app.mOptRecord.isPendingFreeze() || app.mOptRecord.isFrozen();
-        }
-    }
-
     /**
      * Refresh the process queues with the latest process state so that runnableAt
      * can be updated.
@@ -1679,20 +1687,6 @@
     }
 
     /**
-     * Refresh the process queue corresponding to {@code app} with the latest process state
-     * so that runnableAt can be updated.
-     */
-    @GuardedBy("mService")
-    private void refreshProcessQueueLocked(@NonNull ProcessRecord app) {
-        final BroadcastProcessQueue queue = getProcessQueue(app.processName, app.uid);
-        if (queue == null || queue.app == null || queue.app.getPid() != app.getPid()) {
-            return;
-        }
-        setQueueProcess(queue, queue.app);
-        enqueueUpdateRunningList();
-    }
-
-    /**
      * Inform other parts of OS that the given broadcast queue has started
      * running, typically for internal bookkeeping.
      */
@@ -2011,6 +2005,12 @@
         ipw.decreaseIndent();
         ipw.println();
 
+        ipw.println("Cached UIDs:");
+        ipw.increaseIndent();
+        ipw.println(mUidCached);
+        ipw.decreaseIndent();
+        ipw.println();
+
         ipw.println("Foreground UIDs:");
         ipw.increaseIndent();
         ipw.println(mUidForeground);
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index e5a1759..7773190 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -300,7 +300,6 @@
     static final int COMPACT_NATIVE_MSG = 5;
     static final int UID_FROZEN_STATE_CHANGED_MSG = 6;
     static final int DEADLOCK_WATCHDOG_MSG = 7;
-    static final int REPORT_PROCESS_FREEZABLE_CHANGED = 8;
 
     // When free swap falls below this percentage threshold any full (file + anon)
     // compactions will be downgraded to file only compactions to reduce pressure
@@ -1353,7 +1352,6 @@
                 }
             }
         }
-        postProcessFreezableChangedMessage(app);
         mFreezeHandler.sendMessageDelayed(
                 mFreezeHandler.obtainMessage(SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app),
                 delayMillis);
@@ -1391,7 +1389,6 @@
             uidRec.setFrozen(false);
             postUidFrozenMessage(uidRec.getUid(), false);
         }
-        postProcessFreezableChangedMessage(app);
 
         opt.setFreezerOverride(false);
         if (pid == 0 || !opt.isFrozen()) {
@@ -2069,21 +2066,6 @@
                 0, uidObj));
     }
 
-    private void reportProcessFreezableChanged(ProcessRecord app) {
-        synchronized (mAm) {
-            mAm.onProcessFreezableChangedLocked(app);
-        }
-    }
-
-    @GuardedBy("mAm")
-    private void postProcessFreezableChangedMessage(ProcessRecord app) {
-        if (app.getPid() == 0) {
-            return;
-        }
-        mFreezeHandler.obtainMessage(REPORT_PROCESS_FREEZABLE_CHANGED, 0, 0, app)
-                .sendToTarget();
-    }
-
     private final class FreezeHandler extends Handler implements
             ProcLocksReader.ProcLocksReaderCallback {
         private FreezeHandler() {
@@ -2093,7 +2075,7 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
-                case SET_FROZEN_PROCESS_MSG: {
+                case SET_FROZEN_PROCESS_MSG:
                     ProcessRecord proc = (ProcessRecord) msg.obj;
                     synchronized (mAm) {
                         freezeProcess(proc);
@@ -2103,8 +2085,8 @@
                         removeMessages(DEADLOCK_WATCHDOG_MSG);
                         sendEmptyMessageDelayed(DEADLOCK_WATCHDOG_MSG, FREEZE_DEADLOCK_TIMEOUT_MS);
                     }
-                } break;
-                case REPORT_UNFREEZE_MSG: {
+                    break;
+                case REPORT_UNFREEZE_MSG:
                     int pid = msg.arg1;
                     int frozenDuration = msg.arg2;
                     Pair<String, Integer> obj = (Pair<String, Integer>) msg.obj;
@@ -2112,13 +2094,13 @@
                     int reason = obj.second;
 
                     reportUnfreeze(pid, frozenDuration, processName, reason);
-                } break;
-                case UID_FROZEN_STATE_CHANGED_MSG: {
+                    break;
+                case UID_FROZEN_STATE_CHANGED_MSG:
                     final boolean frozen = (msg.arg1 == 1);
                     final int uid = (int) msg.obj;
                     reportOneUidFrozenStateChanged(uid, frozen);
-                } break;
-                case DEADLOCK_WATCHDOG_MSG: {
+                    break;
+                case DEADLOCK_WATCHDOG_MSG:
                     try {
                         // post-check to prevent deadlock
                         if (DEBUG_FREEZER) {
@@ -2128,11 +2110,7 @@
                     } catch (IOException e) {
                         Slog.w(TAG_AM, "Unable to check file locks");
                     }
-                } break;
-                case REPORT_PROCESS_FREEZABLE_CHANGED: {
-                    final ProcessRecord app = (ProcessRecord) msg.obj;
-                    reportProcessFreezableChanged(app);
-                } break;
+                    break;
                 default:
                     return;
             }
diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java
index 844f175..7482e64 100644
--- a/services/core/java/com/android/server/am/CoreSettingsObserver.java
+++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java
@@ -97,10 +97,6 @@
         sGlobalSettingToTypeMap.put(
                 Settings.Global.ANGLE_EGL_FEATURES, String.class);
         sGlobalSettingToTypeMap.put(
-                Settings.Global.ANGLE_DEFERLIST, String.class);
-        sGlobalSettingToTypeMap.put(
-                Settings.Global.ANGLE_DEFERLIST_MODE, String.class);
-        sGlobalSettingToTypeMap.put(
                 Settings.Global.SHOW_ANGLE_IN_USE_DIALOG_BOX, String.class);
         sGlobalSettingToTypeMap.put(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, int.class);
         sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_APP, String.class);
diff --git a/services/core/java/com/android/server/am/DropboxRateLimiter.java b/services/core/java/com/android/server/am/DropboxRateLimiter.java
index 727d4df9..b5c7215 100644
--- a/services/core/java/com/android/server/am/DropboxRateLimiter.java
+++ b/services/core/java/com/android/server/am/DropboxRateLimiter.java
@@ -40,7 +40,7 @@
     // If a process is rate limited twice in a row we consider it crash-looping and rate limit it
     // more aggressively.
     private static final int STRICT_RATE_LIMIT_ALLOWED_ENTRIES = 1;
-    private static final long STRICT_RATE_LIMIT_BUFFER_DURATION = 60 * DateUtils.MINUTE_IN_MILLIS;
+    private static final long STRICT_RATE_LIMIT_BUFFER_DURATION = 20 * DateUtils.MINUTE_IN_MILLIS;
 
     @GuardedBy("mErrorClusterRecords")
     private final ArrayMap<String, ErrorRecord> mErrorClusterRecords = new ArrayMap<>();
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index cb26c13..ab4fb46 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -42,6 +42,7 @@
 import android.os.IBinder;
 import android.os.PowerWhitelistManager;
 import android.os.PowerWhitelistManager.ReasonCode;
+import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.TransactionTooLargeException;
@@ -382,6 +383,14 @@
             })
     public static BackgroundStartPrivileges getDefaultBackgroundStartPrivileges(
             int callingUid) {
+        if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
+            // We temporarily allow BAL for system processes, while we verify that all valid use
+            // cases are opted in explicitly to grant their BAL permission.
+            // Background: In many cases devices are running additional apps that share UID with
+            // the system. If one of these apps targets a lower SDK the change is not active, but
+            // as soon as that app is upgraded (or removed) BAL would be blocked. (b/283138430)
+            return BackgroundStartPrivileges.ALLOW_BAL;
+        }
         boolean isChangeEnabledForApp = CompatChanges.isChangeEnabled(
                 DEFAULT_RESCIND_BAL_PRIVILEGES_FROM_PENDING_INTENT_SENDER, callingUid);
         if (isChangeEnabledForApp) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 9b1149a..335d676 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1192,9 +1192,6 @@
                         "Killing " + toShortString() + " (adj " + mState.getSetAdj()
                         + "): " + reason, info.uid);
             }
-            // Since the process is getting killed, reset the freezable related state.
-            mOptRecord.setPendingFreeze(false);
-            mOptRecord.setFrozen(false);
             if (mPid > 0) {
                 mService.mProcessList.noteAppKill(this, reasonCode, subReason, description);
                 EventLog.writeEvent(EventLogTags.AM_KILL,
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 334c145..12bb5d2 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -3567,6 +3567,8 @@
                 if (mUserSwitchingDialog != null) {
                     mUserSwitchingDialog.dismiss(onDismissed);
                     mUserSwitchingDialog = null;
+                } else if (onDismissed != null) {
+                    onDismissed.run();
                 }
             }
         }
diff --git a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
index 055c63d..46c77e8 100644
--- a/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/AcquisitionClient.java
@@ -206,17 +206,6 @@
         }
     }
 
-    protected final void vibrateError() {
-        Vibrator vibrator = getContext().getSystemService(Vibrator.class);
-        if (vibrator != null && mShouldVibrate) {
-            vibrator.vibrate(Process.myUid(),
-                    getContext().getOpPackageName(),
-                    ERROR_VIBRATION_EFFECT,
-                    getClass().getSimpleName() + "::error",
-                    HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
-        }
-    }
-
     @Override
     public boolean isInterruptable() {
         return true;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
index 9dc1782..a529fb9 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceDetectClient.java
@@ -72,7 +72,7 @@
             boolean isStrongBiometric, SensorPrivacyManager sensorPrivacyManager) {
         super(context, lazyDaemon, token, listener, options.getUserId(),
                 options.getOpPackageName(), 0 /* cookie */, options.getSensorId(),
-                true /* shouldVibrate */, logger, biometricContext);
+                false /* shouldVibrate */, logger, biometricContext);
         setRequestId(requestId);
         mIsStrongBiometric = isStrongBiometric;
         mSensorPrivacyManager = sensorPrivacyManager;
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 38631c8..a97126c 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -232,7 +232,10 @@
                 return;
             }
 
-            if ((serviceTypes & ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION) != 0) {
+            if (mActivityManagerInternal.hasRunningForegroundService(
+                    uid, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION)) {
+                // If there is any process within this UID running a FGS
+                // with the mediaProjection type, that's Okay.
                 return;
             }
 
diff --git a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
index cd457b7..feb75ef 100644
--- a/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
+++ b/services/core/java/com/android/server/notification/NotificationRecordLoggerImpl.java
@@ -74,7 +74,8 @@
                 notificationReported.is_ongoing,
                 notificationReported.is_foreground_service,
                 notificationReported.timeout_millis,
-                notificationReported.is_non_dismissible);
+                notificationReported.is_non_dismissible,
+                notificationReported.post_duration_millis);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
index 0bb05aa..1b34c70 100644
--- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
+++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java
@@ -231,6 +231,14 @@
             return;
         }
 
+        // the installers without INSTALL_PACKAGES perm can't perform
+        // the installation in background. So we can just filter out them.
+        if (mPermissionManager.checkPermission(installerPackageName,
+                android.Manifest.permission.INSTALL_PACKAGES,
+                userId) != PackageManager.PERMISSION_GRANTED) {
+            return;
+        }
+
         // convert up-time to current time.
         final long installTimestamp = System.currentTimeMillis()
                 - (SystemClock.uptimeMillis() - appInfo.createTimestamp);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ae520c0..bba8043 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -279,6 +279,7 @@
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
@@ -6295,6 +6296,36 @@
             }
         }
 
+        /**
+         * Wait for the handler to finish handling all pending messages.
+         * @param timeoutMillis Maximum time in milliseconds to wait.
+         * @param forBackgroundHandler Whether to wait for the background handler instead.
+         * @return True if all the waiting messages in the handler has been handled.
+         *         False if timeout.
+         */
+        @Override
+        public boolean waitForHandler(long timeoutMillis, boolean forBackgroundHandler) {
+            final CountDownLatch latch = new CountDownLatch(1);
+            if (forBackgroundHandler) {
+                mBackgroundHandler.post(latch::countDown);
+            } else {
+                mHandler.post(latch::countDown);
+            }
+            final long endTimeMillis = System.currentTimeMillis() + timeoutMillis;
+            while (latch.getCount() > 0) {
+                try {
+                    final long remainingTimeMillis = endTimeMillis - System.currentTimeMillis();
+                    if (remainingTimeMillis <= 0) {
+                        return false;
+                    }
+                    return latch.await(remainingTimeMillis, TimeUnit.MILLISECONDS);
+                } catch (InterruptedException e) {
+                    // ignore and retry
+                }
+            }
+            return true;
+        }
+
         @Override
         public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
                 throws RemoteException {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 05bfec4..e1f010f 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -354,6 +354,10 @@
                     return runSetSilentUpdatesPolicy();
                 case "get-app-metadata":
                     return runGetAppMetadata();
+                case "wait-for-handler":
+                    return runWaitForHandler(/* forBackgroundHandler= */ false);
+                case "wait-for-background-handler":
+                    return runWaitForHandler(/* forBackgroundHandler= */ true);
                 default: {
                     if (ART_SERVICE_COMMANDS.contains(cmd)) {
                         if (DexOptHelper.useArtService()) {
@@ -3601,6 +3605,40 @@
         return 1;
     }
 
+    private int runWaitForHandler(boolean forBackgroundHandler) {
+        final PrintWriter pw = getOutPrintWriter();
+        long timeoutMillis = 60000; // default timeout is 60 seconds
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "--timeout":
+                    timeoutMillis = Long.parseLong(getNextArgRequired());
+                    break;
+                default:
+                    pw.println("Error: Unknown option: " + opt);
+                    return -1;
+            }
+        }
+        if (timeoutMillis <= 0) {
+            pw.println("Error: --timeout value must be positive: " + timeoutMillis);
+            return -1;
+        }
+        final boolean success;
+        try {
+            success = mInterface.waitForHandler(timeoutMillis, forBackgroundHandler);
+        } catch (RemoteException e) {
+            pw.println("Failure [" + e.getClass().getName() + " - " + e.getMessage() + "]");
+            return -1;
+        }
+        if (success) {
+            pw.println("Success");
+            return 0;
+        } else {
+            pw.println("Timeout. PackageManager handlers are still busy.");
+            return -1;
+        }
+    }
+
     private int runArtServiceCommand() {
         try (var in = ParcelFileDescriptor.dup(getInFileDescriptor());
                 var out = ParcelFileDescriptor.dup(getOutFileDescriptor());
@@ -4427,6 +4465,18 @@
         pw.println("      --reset: restore the installer and throttle time to the default, and");
         pw.println("        clear tracks of silent updates in the system.");
         pw.println("");
+        pw.println("  wait-for-handler --timeout <MILLIS>");
+        pw.println("    Wait for a given amount of time till the package manager handler finishes");
+        pw.println("    handling all pending messages.");
+        pw.println("      --timeout: wait for a given number of milliseconds. If the handler(s)");
+        pw.println("        fail to finish before the timeout, the command returns error.");
+        pw.println("");
+        pw.println("  wait-for-background-handler --timeout <MILLIS>");
+        pw.println("    Wait for a given amount of time till the package manager's background");
+        pw.println("    handler finishes handling all pending messages.");
+        pw.println("      --timeout: wait for a given number of milliseconds. If the handler(s)");
+        pw.println("        fail to finish before the timeout, the command returns error.");
+        pw.println("");
         if (DexOptHelper.useArtService()) {
             printArtServiceHelp();
         } else {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index f41d964..3e7ae33 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -2810,29 +2810,55 @@
                                 + pkg.getPackageName());
                     }
 
-                    if ((bp.isNormal() && shouldGrantNormalPermission)
-                            || (bp.isSignature()
-                                    && (!bp.isPrivileged() || CollectionUtils.contains(
-                                            isPrivilegedPermissionAllowlisted, permName))
-                                    && (CollectionUtils.contains(shouldGrantSignaturePermission,
-                                            permName)
-                                            || (((bp.isPrivileged() && CollectionUtils.contains(
-                                                    shouldGrantPrivilegedPermissionIfWasGranted,
-                                                    permName)) || bp.isDevelopment() || bp.isRole())
-                                                    && origState.isPermissionGranted(permName))))
-                            || (bp.isInternal()
-                                    && (!bp.isPrivileged() || CollectionUtils.contains(
-                                            isPrivilegedPermissionAllowlisted, permName))
-                                    && (CollectionUtils.contains(shouldGrantInternalPermission,
-                                            permName)
-                                            || (((bp.isPrivileged() && CollectionUtils.contains(
-                                                    shouldGrantPrivilegedPermissionIfWasGranted,
-                                                    permName)) || bp.isDevelopment() || bp.isRole())
-                                                    && origState.isPermissionGranted(permName))))) {
-                        // Grant an install permission.
-                        if (uidState.grantPermission(bp)) {
-                            installPermissionsChangedForUser = true;
+                    if (bp.isNormal() || bp.isSignature() || bp.isInternal()) {
+                        if ((bp.isNormal() && shouldGrantNormalPermission)
+                                || (bp.isSignature()
+                                        && (!bp.isPrivileged() || CollectionUtils.contains(
+                                                isPrivilegedPermissionAllowlisted, permName))
+                                        && (CollectionUtils.contains(shouldGrantSignaturePermission,
+                                                permName)
+                                                || (((bp.isPrivileged() && CollectionUtils.contains(
+                                                        shouldGrantPrivilegedPermissionIfWasGranted,
+                                                        permName)) || bp.isDevelopment()
+                                                                || bp.isRole())
+                                                        && origState.isPermissionGranted(
+                                                                permName))))
+                                || (bp.isInternal()
+                                        && (!bp.isPrivileged() || CollectionUtils.contains(
+                                                isPrivilegedPermissionAllowlisted, permName))
+                                        && (CollectionUtils.contains(shouldGrantInternalPermission,
+                                                permName)
+                                                || (((bp.isPrivileged() && CollectionUtils.contains(
+                                                        shouldGrantPrivilegedPermissionIfWasGranted,
+                                                        permName)) || bp.isDevelopment()
+                                                                || bp.isRole())
+                                                        && origState.isPermissionGranted(
+                                                                permName))))) {
+                            // Grant an install permission.
+                            if (uidState.grantPermission(bp)) {
+                                installPermissionsChangedForUser = true;
+                            }
+                        } else {
+                            if (DEBUG_PERMISSIONS) {
+                                boolean wasGranted = uidState.isPermissionGranted(bp.getName());
+                                if (wasGranted || bp.isAppOp()) {
+                                    Slog.i(TAG, (wasGranted ? "Un-granting" : "Not granting")
+                                            + " permission " + perm
+                                            + " from package " + friendlyName
+                                            + " (protectionLevel=" + bp.getProtectionLevel()
+                                            + " flags=0x"
+                                            + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg,
+                                            ps))
+                                            + ")");
+                                }
+                            }
+                            if (uidState.revokePermission(bp)) {
+                                installPermissionsChangedForUser = true;
+                            }
                         }
+                        PermissionState origPermState = origState.getPermissionState(perm);
+                        int flags = origPermState != null ? origPermState.getFlags() : 0;
+                        uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL, flags);
                     } else if (bp.isRuntime()) {
                         boolean hardRestricted = bp.isHardRestricted();
                         boolean softRestricted = bp.isSoftRestricted();
@@ -2956,22 +2982,8 @@
                         uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL,
                                 flags);
                     } else {
-                        if (DEBUG_PERMISSIONS) {
-                            boolean wasGranted = uidState.isPermissionGranted(bp.getName());
-                            if (wasGranted || bp.isAppOp()) {
-                                Slog.i(TAG, (wasGranted ? "Un-granting" : "Not granting")
-                                        + " permission " + perm
-                                        + " from package " + friendlyName
-                                        + " (protectionLevel=" + bp.getProtectionLevel()
-                                        + " flags=0x"
-                                        + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg,
-                                                ps))
-                                        + ")");
-                            }
-                        }
-                        if (uidState.removePermissionState(bp.getName())) {
-                            installPermissionsChangedForUser = true;
-                        }
+                        Slog.wtf(LOG_TAG, "Unknown permission protection " + bp.getProtection()
+                                + " for permission " + bp.getName());
                     }
                 }
 
diff --git a/services/core/java/com/android/server/uri/UriPermission.java b/services/core/java/com/android/server/uri/UriPermission.java
index 6db781a..f3eeab0 100644
--- a/services/core/java/com/android/server/uri/UriPermission.java
+++ b/services/core/java/com/android/server/uri/UriPermission.java
@@ -207,7 +207,9 @@
             if (mReadOwners != null && includingOwners) {
                 ownedModeFlags &= ~Intent.FLAG_GRANT_READ_URI_PERMISSION;
                 for (UriPermissionOwner r : mReadOwners) {
-                    r.removeReadPermission(this);
+                    if (r != null) {
+                        r.removeReadPermission(this);
+                    }
                 }
                 mReadOwners = null;
             }
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 6b90181..aafff2c 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
 import static android.app.Activity.FULLSCREEN_MODE_REQUEST_ENTER;
+import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.ActivityTaskManager.INVALID_WINDOWING_MODE;
 import static android.app.FullscreenRequestHandler.REMOTE_CALLBACK_RESULT_KEY;
@@ -818,6 +819,13 @@
                                 null /*startTask */, null /* remoteTransition */,
                                 null /* displayChange */);
                         r.mTransitionController.setReady(r.getDisplayContent());
+                        if (under != null && under.returningOptions != null
+                                && under.returningOptions.getAnimationType()
+                                        == ANIM_SCENE_TRANSITION) {
+                            // Pass along the scene-transition animation-type
+                            transition.setOverrideAnimation(TransitionInfo.AnimationOptions
+                                    .makeSceneTransitionAnimOptions(), null, null);
+                        }
                     } else {
                         transition.abort();
                     }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index acfa30c..d84c013 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3560,7 +3560,7 @@
                 // Note that RecentsAnimation will handle task snapshot while switching apps with
                 // the best capture timing (e.g. IME window capture),
                 // No need additional task capture while task is controlled by RecentsAnimation.
-                if (mAtmService.mWindowManager.mTaskSnapshotController != null
+                if (!mTransitionController.isShellTransitionsEnabled()
                         && !task.isAnimatingByRecents()) {
                     final ArraySet<Task> tasks = Sets.newArraySet(task);
                     mAtmService.mWindowManager.mTaskSnapshotController.snapshotTasks(tasks);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index cff6554..1360a95 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3789,7 +3789,7 @@
             TaskSnapshot taskSnapshot = mWindowManager.mTaskSnapshotController.getSnapshot(taskId,
                     task.mUserId, true /* restoreFromDisk */, isLowResolution);
             if (taskSnapshot == null && takeSnapshotIfNeeded) {
-                taskSnapshot = takeTaskSnapshot(taskId);
+                taskSnapshot = takeTaskSnapshot(taskId, false /* updateCache */);
             }
             return taskSnapshot;
         } finally {
@@ -3798,7 +3798,7 @@
     }
 
     @Override
-    public TaskSnapshot takeTaskSnapshot(int taskId) {
+    public TaskSnapshot takeTaskSnapshot(int taskId, boolean updateCache) {
         mAmInternal.enforceCallingPermission(READ_FRAME_BUFFER, "takeTaskSnapshot()");
         final long ident = Binder.clearCallingIdentity();
         try {
@@ -3809,8 +3809,13 @@
                     Slog.w(TAG, "takeTaskSnapshot: taskId=" + taskId + " not found or not visible");
                     return null;
                 }
-                return mWindowManager.mTaskSnapshotController.captureSnapshot(
-                        task, true /* snapshotHome */);
+                if (updateCache) {
+                    return mWindowManager.mTaskSnapshotController.recordSnapshot(task,
+                            true /* snapshotHome */);
+                } else {
+                    return mWindowManager.mTaskSnapshotController.captureSnapshot(task,
+                            true /* snapshotHome */);
+                }
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index b67ccd2..cea886f 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -440,7 +440,8 @@
                         if (app != null) {
                             mDisplayContent.removeImeSurfaceImmediately();
                             if (app.getTask() != null) {
-                                mDisplayContent.mAtmService.takeTaskSnapshot(app.getTask().mTaskId);
+                                mDisplayContent.mAtmService.takeTaskSnapshot(app.getTask().mTaskId,
+                                        true /* updateCache */);
                             }
                         }
                     } else {
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 4d0bff9..7e20b3b 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -28,6 +28,7 @@
 import android.os.Environment;
 import android.os.Handler;
 import android.util.ArraySet;
+import android.util.IntArray;
 import android.util.Slog;
 import android.view.Display;
 import android.window.ScreenCapture;
@@ -37,8 +38,6 @@
 import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
 import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider;
 
-import com.google.android.collect.Sets;
-
 import java.util.Set;
 
 /**
@@ -58,7 +57,7 @@
     static final String SNAPSHOTS_DIRNAME = "snapshots";
 
     private final TaskSnapshotPersister mPersister;
-    private final ArraySet<Task> mSkipClosingAppSnapshotTasks = new ArraySet<>();
+    private final IntArray mSkipClosingAppSnapshotTasks = new IntArray();
     private final ArraySet<Task> mTmpTasks = new ArraySet<>();
     private final Handler mHandler = new Handler();
 
@@ -135,26 +134,6 @@
     }
 
     /**
-     * Called when the visibility of an app changes outside of the regular app transition flow.
-     */
-    void notifyAppVisibilityChanged(ActivityRecord appWindowToken, boolean visible) {
-        if (!visible) {
-            handleClosingApps(Sets.newArraySet(appWindowToken));
-        }
-    }
-
-    private void handleClosingApps(ArraySet<ActivityRecord> closingApps) {
-        if (shouldDisableSnapshots()) {
-            return;
-        }
-        // We need to take a snapshot of the task if and only if all activities of the task are
-        // either closing or hidden.
-        getClosingTasks(closingApps, mTmpTasks);
-        snapshotTasks(mTmpTasks);
-        mSkipClosingAppSnapshotTasks.clear();
-    }
-
-    /**
      * Adds the given {@param tasks} to the list of tasks which should not have their snapshots
      * taken upon the next processing of the set of closing apps. The caller is responsible for
      * calling {@link #snapshotTasks} to ensure that the task has an up-to-date snapshot.
@@ -164,20 +143,23 @@
         if (shouldDisableSnapshots()) {
             return;
         }
-        mSkipClosingAppSnapshotTasks.addAll(tasks);
+        for (Task task : tasks) {
+            mSkipClosingAppSnapshotTasks.add(task.mTaskId);
+        }
     }
 
     void snapshotTasks(ArraySet<Task> tasks) {
         snapshotTasks(tasks, false /* allowSnapshotHome */);
     }
 
-    void recordSnapshot(Task task, boolean allowSnapshotHome) {
+    TaskSnapshot recordSnapshot(Task task, boolean allowSnapshotHome) {
         final boolean snapshotHome = allowSnapshotHome && task.isActivityTypeHome();
         final TaskSnapshot snapshot = recordSnapshotInner(task, allowSnapshotHome);
         if (!snapshotHome && snapshot != null) {
             mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
             task.onSnapshotChanged(snapshot);
         }
+        return snapshot;
     }
 
     private void snapshotTasks(ArraySet<Task> tasks, boolean allowSnapshotHome) {
@@ -271,31 +253,16 @@
         return source.getTaskDescription();
     }
 
-    /**
-     * Retrieves all closing tasks based on the list of closing apps during an app transition.
-     */
-    @VisibleForTesting
-    void getClosingTasks(ArraySet<ActivityRecord> closingApps, ArraySet<Task> outClosingTasks) {
-        outClosingTasks.clear();
-        for (int i = closingApps.size() - 1; i >= 0; i--) {
-            final ActivityRecord activity = closingApps.valueAt(i);
-            final Task task = activity.getTask();
-            if (task == null) continue;
-
-            getClosingTasksInner(task, outClosingTasks);
-        }
-    }
-
     void getClosingTasksInner(Task task, ArraySet<Task> outClosingTasks) {
         // Since RecentsAnimation will handle task snapshot while switching apps with the
         // best capture timing (e.g. IME window capture),
         // No need additional task capture while task is controlled by RecentsAnimation.
         if (isAnimatingByRecents(task)) {
-            mSkipClosingAppSnapshotTasks.add(task);
+            mSkipClosingAppSnapshotTasks.add(task.mTaskId);
         }
         // If the task of the app is not visible anymore, it means no other app in that task
         // is opening. Thus, the task is closing.
-        if (!task.isVisible() && !mSkipClosingAppSnapshotTasks.contains(task)) {
+        if (!task.isVisible() && mSkipClosingAppSnapshotTasks.indexOf(task.mTaskId) < 0) {
             outClosingTasks.add(task);
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 09312ba..7e34d15 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -845,11 +845,14 @@
             case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: {
                 final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
                 final Task task = wc != null ? wc.asTask() : null;
-                if (task != null) {
+                if (task == null) {
+                    throw new IllegalArgumentException("Cannot set non-task as launch root: " + wc);
+                } else if (task.getTaskDisplayArea() == null) {
+                    throw new IllegalArgumentException("Cannot set a task without display area as "
+                            + "launch root: " + wc);
+                } else {
                     task.getDisplayArea().setLaunchRootTask(task,
                             hop.getWindowingModes(), hop.getActivityTypes());
-                } else {
-                    throw new IllegalArgumentException("Cannot set non-task as launch root: " + wc);
                 }
                 break;
             }
diff --git a/services/credentials/java/com/android/server/credentials/metrics/ApiName.java b/services/credentials/java/com/android/server/credentials/metrics/ApiName.java
index cb6a5d0..d828349 100644
--- a/services/credentials/java/com/android/server/credentials/metrics/ApiName.java
+++ b/services/credentials/java/com/android/server/credentials/metrics/ApiName.java
@@ -30,6 +30,7 @@
 import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_REGISTER_CREDENTIAL_DESCRIPTION;
 import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_SET_ENABLED_PROVIDERS;
 import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_UNREGISTER_CREDENTIAL_DESCRIPTION;
 
 import android.credentials.ui.RequestInfo;
 import android.util.Slog;
@@ -61,7 +62,7 @@
     ),
 
     UNREGISTER_CREDENTIAL_DESCRIPTION(
-    CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_REGISTER_CREDENTIAL_DESCRIPTION
+    CREDENTIAL_MANAGER_INITIAL_PHASE_REPORTED__API_NAME__API_NAME_UNREGISTER_CREDENTIAL_DESCRIPTION
     );
 
     private static final String TAG = "ApiName";
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index cf7c718..79c0349 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -1092,12 +1092,9 @@
                 if (policies.get(admin).getValue() != null
                         && policies.get(admin).getValue().getPackageName().equals(packageName)) {
                     try {
-                        if (packageManager.getPackageInfo(
-                                packageName, 0, userId) == null
-                                || packageManager.getReceiverInfo(policies.get(admin).getValue(),
-                                PackageManager.MATCH_DIRECT_BOOT_AWARE
-                                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
-                                userId) == null) {
+                        if (packageManager.getPackageInfo(packageName, 0, userId) == null
+                                || packageManager.getActivityInfo(
+                                        policies.get(admin).getValue(), 0, userId) == null) {
                             Slogf.e(TAG, String.format(
                                     "Persistent preferred activity in package %s not found for "
                                             + "user %d, removing policy for admin",
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index 2b44615..f47954b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -393,9 +393,9 @@
                 List.of(makeMockRegisteredReceiver()), false);
         enqueueOrReplaceBroadcast(queue, airplaneRecord, 0);
 
-        queue.setProcessAndUidState(mProcess, false, false);
+        queue.setProcessAndUidState(null, false, false);
         final long notCachedRunnableAt = queue.getRunnableAt();
-        queue.setProcessAndUidState(mProcess, false, true);
+        queue.setProcessAndUidState(null, false, true);
         final long cachedRunnableAt = queue.getRunnableAt();
         assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt);
         assertFalse(queue.isRunnable());
@@ -420,9 +420,9 @@
                 List.of(makeMockRegisteredReceiver()), false);
         enqueueOrReplaceBroadcast(queue, airplaneRecord, 0);
 
-        queue.setProcessAndUidState(mProcess, false, false);
+        queue.setProcessAndUidState(null, false, false);
         final long notCachedRunnableAt = queue.getRunnableAt();
-        queue.setProcessAndUidState(mProcess, false, true);
+        queue.setProcessAndUidState(null, false, true);
         final long cachedRunnableAt = queue.getRunnableAt();
         assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt);
         assertTrue(queue.isRunnable());
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 615b4c1..ad5f0d7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -399,12 +399,6 @@
                 UserHandle.USER_SYSTEM);
     }
 
-    private ProcessRecord makeActiveProcessRecord(String packageName, String processName)
-            throws Exception {
-        return makeActiveProcessRecord(packageName, processName, ProcessBehavior.NORMAL,
-                UserHandle.USER_SYSTEM);
-    }
-
     private ProcessRecord makeActiveProcessRecord(String packageName,
             ProcessBehavior behavior) throws Exception {
         return makeActiveProcessRecord(packageName, packageName, behavior, UserHandle.USER_SYSTEM);
@@ -602,11 +596,6 @@
                 BackgroundStartPrivileges.NONE, false, null);
     }
 
-    private void setProcessFreezable(ProcessRecord app, boolean pendingFreeze, boolean frozen) {
-        app.mOptRecord.setPendingFreeze(pendingFreeze);
-        app.mOptRecord.setFrozen(frozen);
-    }
-
     private void assertHealth() {
         if (mImpl == Impl.MODERN) {
             // If this fails, it'll throw a clear reason message
@@ -1670,10 +1659,8 @@
         final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW);
         final ProcessRecord receiverOrangeApp = makeActiveProcessRecord(PACKAGE_ORANGE);
 
-        setProcessFreezable(receiverGreenApp, true, false);
-        mQueue.onProcessFreezableChangedLocked(receiverGreenApp);
-        setProcessFreezable(receiverBlueApp, false, true);
-        mQueue.onProcessFreezableChangedLocked(receiverBlueApp);
+        mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), true);
+        mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_BLUE), true);
 
         final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
         final BroadcastOptions opts = BroadcastOptions.makeBasic()
@@ -1717,14 +1704,12 @@
                 eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
 
         // Shift blue to be active and confirm that deferred broadcast is delivered
-        setProcessFreezable(receiverBlueApp, false, false);
-        mQueue.onProcessFreezableChangedLocked(receiverBlueApp);
+        mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_BLUE), false);
         waitForIdle();
         verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, timeTick);
 
         // Shift green to be active and confirm that deferred broadcast is delivered
-        setProcessFreezable(receiverGreenApp, false, false);
-        mQueue.onProcessFreezableChangedLocked(receiverGreenApp);
+        mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), false);
         waitForIdle();
         verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, timeTick);
     }
@@ -2121,12 +2106,9 @@
         final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
         final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW);
 
-        setProcessFreezable(receiverGreenApp, true, true);
-        mQueue.onProcessFreezableChangedLocked(receiverGreenApp);
-        setProcessFreezable(receiverBlueApp, true, false);
-        mQueue.onProcessFreezableChangedLocked(receiverBlueApp);
-        setProcessFreezable(receiverYellowApp, false, false);
-        mQueue.onProcessFreezableChangedLocked(receiverYellowApp);
+        mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), true);
+        mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_BLUE), true);
+        mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_YELLOW), false);
 
         final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
         final BroadcastOptions opts = BroadcastOptions.makeBasic()
@@ -2149,50 +2131,11 @@
         verifyScheduleRegisteredReceiver(times(1), receiverYellowApp, airplane);
 
         // Shift green to be active and confirm that deferred broadcast is delivered
-        setProcessFreezable(receiverGreenApp, false, false);
-        mQueue.onProcessFreezableChangedLocked(receiverGreenApp);
+        mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), false);
         waitForIdle();
         verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, airplane);
     }
 
-    /**
-     * Verify broadcasts to a runtime receiver in cached process is deferred even when a different
-     * process in the same package is not cached.
-     */
-    @Test
-    public void testDeferralPolicy_UntilActive_WithMultiProcessUid() throws Exception {
-        // Legacy stack doesn't support deferral
-        Assume.assumeTrue(mImpl == Impl.MODERN);
-
-        final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
-        final ProcessRecord receiverGreenApp1 = makeActiveProcessRecord(PACKAGE_GREEN);
-        final ProcessRecord receiverGreenApp2 = makeActiveProcessRecord(PACKAGE_GREEN,
-                PACKAGE_GREEN + "_proc2");
-
-        setProcessFreezable(receiverGreenApp1, true, true);
-        mQueue.onProcessFreezableChangedLocked(receiverGreenApp1);
-
-        final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        final BroadcastOptions opts = BroadcastOptions.makeBasic()
-                .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE);
-        enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, opts,
-                List.of(makeRegisteredReceiver(receiverGreenApp1),
-                        makeRegisteredReceiver(receiverGreenApp2))));
-        waitForIdle();
-
-        // 1st process in Green package is ignored since it is in a cached state
-        // but the 2nd process should still receive the broadcast.
-        verifyScheduleRegisteredReceiver(never(), receiverGreenApp1, airplane);
-        verifyScheduleRegisteredReceiver(times(1), receiverGreenApp2, airplane);
-
-        // Shift the 1st process in Green package to be active and confirm that deferred broadcast
-        // is delivered
-        setProcessFreezable(receiverGreenApp1, false, false);
-        mQueue.onProcessFreezableChangedLocked(receiverGreenApp1);
-        waitForIdle();
-        verifyScheduleRegisteredReceiver(times(1), receiverGreenApp1, airplane);
-    }
-
     @Test
     public void testBroadcastDelivery_uidForeground() throws Exception {
         // Legacy stack doesn't support prioritization to foreground app.
diff --git a/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java b/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
index 01563e2..36a565e 100644
--- a/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/DropboxRateLimiterTest.java
@@ -140,19 +140,19 @@
         // Repeated crashes after the last reset being rate limited should be restricted faster.
         assertTrue(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
 
-        // We now need to wait 61 minutes for the buffer should be empty again.
-        mClock.setOffsetMillis(83 * 60 * 1000);
+        // We now need to wait 21 minutes for the buffer should be empty again.
+        mClock.setOffsetMillis(43 * 60 * 1000);
         assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
 
-        // After yet another 61 minutes, this time without triggering rate limiting, the strict
+        // After yet another 21 minutes, this time without triggering rate limiting, the strict
         // limiting should be turnd off.
-        mClock.setOffsetMillis(144 * 60 * 1000);
+        mClock.setOffsetMillis(64 * 60 * 1000);
         assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
         assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
 
         // As rate limiting was not triggered in the last reset, after another 11 minutes the
         // buffer should still act as normal.
-        mClock.setOffsetMillis(155 * 60 * 1000);
+        mClock.setOffsetMillis(75 * 60 * 1000);
         // The first 6 entries should not be rate limited.
         assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
         assertFalse(mRateLimiter.shouldRateLimit("tag", "process").shouldRateLimit());
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
index c26eee9..ade3e82 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceDetectClientTest.java
@@ -24,6 +24,7 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.same;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
 import android.hardware.biometrics.common.AuthenticateReason;
@@ -34,6 +35,7 @@
 import android.os.IBinder;
 import android.os.PowerManager;
 import android.os.RemoteException;
+import android.os.Vibrator;
 import android.platform.test.annotations.Presubmit;
 import android.testing.TestableContext;
 
@@ -75,6 +77,8 @@
     @Mock
     private IBinder mToken;
     @Mock
+    private Vibrator mVibrator;
+    @Mock
     private ClientMonitorCallbackConverter mClientMonitorCallbackConverter;
     @Mock
     private BiometricLogger mBiometricLogger;
@@ -94,6 +98,8 @@
 
     @Before
     public void setup() {
+        mContext.addMockSystemService(Vibrator.class, mVibrator);
+
         when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer(
                 i -> i.getArgument(0));
     }
@@ -147,6 +153,16 @@
         verify(mBiometricContext).unsubscribe(same(mOperationContextCaptor.getValue()));
     }
 
+    @Test
+    public void doesNotPlayHapticOnInteractionDetected() throws Exception {
+        final FaceDetectClient client = createClient();
+        client.start(mCallback);
+        client.onInteractionDetected();
+        client.stopHalOperation();
+
+        verifyZeroInteractions(mVibrator);
+    }
+
     private FaceDetectClient createClient() throws RemoteException {
         return createClient(100 /* version */);
     }
diff --git a/services/tests/servicestests/src/com/android/server/credentials/MetricUtilitiesTest.java b/services/tests/servicestests/src/com/android/server/credentials/MetricUtilitiesTest.java
new file mode 100644
index 0000000..b46f1a6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/credentials/MetricUtilitiesTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.credentials;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.credentials.metrics.InitialPhaseMetric;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Given the secondary-system nature of the MetricUtilities, we expect absolutely nothing to
+ * throw an error. If one presents itself, that is problematic.
+ *
+ * atest FrameworksServicesTests:com.android.server.credentials.MetricUtilitiesTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public final class MetricUtilitiesTest {
+
+    @Test
+    public void logApiCalledInitialPhase_nullInitPhaseMetricAndNegativeSequence_success() {
+        MetricUtilities.logApiCalledInitialPhase(null, -1);
+    }
+
+    @Test
+    public void logApiCalledInitialPhase_invalidInitPhaseMetricAndPositiveSequence_success() {
+        MetricUtilities.logApiCalledInitialPhase(new InitialPhaseMetric(-1), 1);
+    }
+
+    @Test
+    public void logApiCalledInitialPhase_validInitPhaseMetric_success() {
+        InitialPhaseMetric validInitPhaseMetric = new InitialPhaseMetric(
+                MetricUtilities.getHighlyUniqueInteger());
+        MetricUtilities.logApiCalledInitialPhase(validInitPhaseMetric, 1);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/credentials/metrics/ApiNameTest.java b/services/tests/servicestests/src/com/android/server/credentials/metrics/ApiNameTest.java
new file mode 100644
index 0000000..8093b18
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/credentials/metrics/ApiNameTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.credentials.metrics;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ApiNameTest {
+
+    @Test
+    public void getMetricCode_matchesWestWorldMetricCode_success() {
+        // com.android.os is only visible in cts tests, so we need to mimic it for server side unit
+        // tests. aidegen can identify it but builds will not. Thus, this is the workaround.
+        Set<Integer> expectedApiNames = new HashSet<>();
+        for (int i = 0; i < 10; i++) {
+            expectedApiNames.add(i);
+        }
+        Set<Integer> actualServerApiNames = Arrays.stream(ApiName.values())
+                .map(ApiName::getMetricCode).collect(Collectors.toSet());
+
+        assertThat(actualServerApiNames).containsExactlyElementsIn(expectedApiNames);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/credentials/metrics/ApiStatusTest.java b/services/tests/servicestests/src/com/android/server/credentials/metrics/ApiStatusTest.java
new file mode 100644
index 0000000..3bf4814
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/credentials/metrics/ApiStatusTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.credentials.metrics;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ApiStatusTest {
+
+    @Test
+    public void getMetricCode_matchesWestWorldMetricCode_success() {
+        Set<Integer> expectedApiStatus = new HashSet<>();
+        for (int i = 1; i < 5; i++) {
+            expectedApiStatus.add(i);
+        }
+        Set<Integer> actualServerApiStatus = Arrays.stream(ApiStatus.values())
+                .map(ApiStatus::getMetricCode).collect(Collectors.toSet());
+
+        assertThat(actualServerApiStatus).containsExactlyElementsIn(expectedApiStatus);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/credentials/metrics/EntryEnumTest.java b/services/tests/servicestests/src/com/android/server/credentials/metrics/EntryEnumTest.java
new file mode 100644
index 0000000..3104cc2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/credentials/metrics/EntryEnumTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.credentials.metrics;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class EntryEnumTest {
+
+    @Test
+    public void getMetricCode_matchesWestWorldMetricCode_success() {
+        Set<Integer> expectedEntryEnum = new HashSet<>();
+        for (int i = 0; i < 5; i++) {
+            expectedEntryEnum.add(i);
+        }
+        Set<Integer> actualServerEntryEnum = Arrays.stream(EntryEnum.values())
+                .map(EntryEnum::getMetricCode).collect(Collectors.toSet());
+
+        assertThat(actualServerEntryEnum).containsExactlyElementsIn(expectedEntryEnum);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 7092b0b..6e52af1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -298,8 +298,6 @@
         assertTrue(mController.isAnimatingTask(activity.getTask()));
 
         spyOn(mWm.mTaskSnapshotController);
-        doNothing().when(mWm.mTaskSnapshotController).notifyAppVisibilityChanged(any(),
-                anyBoolean());
         doReturn(mMockTaskSnapshot).when(mWm.mTaskSnapshotController).getSnapshot(anyInt(),
                 anyInt(), eq(false) /* restoreFromDisk */, eq(false) /* isLowResolution */);
         mController.setDeferredCancel(true /* deferred */, true /* screenshot */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index 4c7b0aa0..91256ee 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -75,7 +75,7 @@
         final ArraySet<ActivityRecord> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mActivityRecord);
         final ArraySet<Task> closingTasks = new ArraySet<>();
-        mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
+        getClosingTasks(closingApps, closingTasks);
         assertEquals(1, closingTasks.size());
         assertEquals(closingWindow.mActivityRecord.getTask(), closingTasks.valueAt(0));
     }
@@ -93,7 +93,7 @@
         final ArraySet<ActivityRecord> closingApps = new ArraySet<>();
         closingApps.add(closingWindow.mActivityRecord);
         final ArraySet<Task> closingTasks = new ArraySet<>();
-        mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
+        getClosingTasks(closingApps, closingTasks);
         assertEquals(0, closingTasks.size());
     }
 
@@ -108,10 +108,23 @@
         final ArraySet<Task> closingTasks = new ArraySet<>();
         mWm.mTaskSnapshotController.addSkipClosingAppSnapshotTasks(
                 Sets.newArraySet(closingWindow.mActivityRecord.getTask()));
-        mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
+        getClosingTasks(closingApps, closingTasks);
         assertEquals(0, closingTasks.size());
     }
 
+    /** Retrieves all closing tasks based on the list of closing apps during an app transition. */
+    private void getClosingTasks(ArraySet<ActivityRecord> closingApps,
+            ArraySet<Task> outClosingTasks) {
+        outClosingTasks.clear();
+        for (int i = closingApps.size() - 1; i >= 0; i--) {
+            final ActivityRecord activity = closingApps.valueAt(i);
+            final Task task = activity.getTask();
+            if (task == null) continue;
+
+            mWm.mTaskSnapshotController.getClosingTasksInner(task, outClosingTasks);
+        }
+    }
+
     @Test
     public void testGetSnapshotMode() {
         final WindowState disabledWindow = createWindow(null,