Merge "Add resident shared memory to the ANR Memory headers" into udc-dev
diff --git a/apct-tests/perftests/multiuser/Android.bp b/apct-tests/perftests/multiuser/Android.bp
index c967e51..45c6b8c 100644
--- a/apct-tests/perftests/multiuser/Android.bp
+++ b/apct-tests/perftests/multiuser/Android.bp
@@ -31,6 +31,9 @@
     ],
     platform_apis: true,
     test_suites: ["device-tests"],
-    data: ["trace_configs/*"],
+    data: [
+        ":MultiUserPerfDummyApp",
+        "trace_configs/*",
+    ],
     certificate: "platform",
 }
diff --git a/apct-tests/perftests/packagemanager/Android.bp b/apct-tests/perftests/packagemanager/Android.bp
index 81cec91..e84aea1 100644
--- a/apct-tests/perftests/packagemanager/Android.bp
+++ b/apct-tests/perftests/packagemanager/Android.bp
@@ -33,7 +33,10 @@
 
     test_suites: ["device-tests"],
 
-    data: [":perfetto_artifacts"],
+    data: [
+        ":QueriesAll0",
+        ":perfetto_artifacts",
+    ],
 
     certificate: "platform",
 
diff --git a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
index 3fc87d3..ce381b6 100644
--- a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
+++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
@@ -22,7 +22,7 @@
 import android.app.AppOpsManager;
 import android.app.AppOpsManager.PackageOps;
 import android.app.IActivityManager;
-import android.app.IUidObserver;
+import android.app.UidObserver;
 import android.app.usage.UsageStatsManager;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -716,11 +716,7 @@
         return true;
     }
 
-    private final class UidObserver extends IUidObserver.Stub {
-        @Override
-        public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
-        }
-
+    private final class UidObserver extends android.app.UidObserver {
         @Override
         public void onUidActive(int uid) {
             mHandler.onUidActive(uid);
@@ -740,10 +736,6 @@
         public void onUidCachedChanged(int uid, boolean cached) {
             mHandler.onUidCachedChanged(uid, cached);
         }
-
-        @Override
-        public void onUidProcAdjChanged(int uid) {
-        }
     }
 
     private final class AppOpsWatcher extends IAppOpsCallback.Stub {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 70b06cb..887ee5f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1551,16 +1551,21 @@
                     jobStatus.getNumPreviousAttempts(),
                     jobStatus.getJob().getMaxExecutionDelayMillis(),
                     /* isDeadlineConstraintSatisfied */ false,
-                    /* isCharging */ false,
-                    /* batteryNotLow */ false,
-                    /* storageNotLow */false,
+                    /* isChargingSatisfied */ false,
+                    /* batteryNotLowSatisfied */ false,
+                    /* storageNotLowSatisfied */false,
                     /* timingDelayConstraintSatisfied */ false,
-                    /* isDeviceIdle */ false,
+                    /* isDeviceIdleSatisfied */ false,
                     /* hasConnectivityConstraintSatisfied */ false,
                     /* hasContentTriggerConstraintSatisfied */ false,
-                    0,
+                    /* jobStartLatencyMs */ 0,
                     jobStatus.getJob().isUserInitiated(),
-                    /* isRunningAsUserInitiatedJob */ false);
+                    /* isRunningAsUserInitiatedJob */ false,
+                    jobStatus.getJob().isPeriodic(),
+                    jobStatus.getJob().getMinLatencyMillis(),
+                    jobStatus.getEstimatedNetworkDownloadBytes(),
+                    jobStatus.getEstimatedNetworkUploadBytes(),
+                    jobStatus.getWorkCount());
 
             // If the job is immediately ready to run, then we can just immediately
             // put it in the pending list and try to schedule it.  This is especially
@@ -1981,9 +1986,14 @@
                     cancelled.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE),
                     cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY),
                     cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER),
-                    0,
+                    /* jobStartLatencyMs */ 0,
                     cancelled.getJob().isUserInitiated(),
-                    /* isRunningAsUserInitiatedJob */ false);
+                    /* isRunningAsUserInitiatedJob */ false,
+                    cancelled.getJob().isPeriodic(),
+                    cancelled.getJob().getMinLatencyMillis(),
+                    cancelled.getEstimatedNetworkDownloadBytes(),
+                    cancelled.getEstimatedNetworkUploadBytes(),
+                    cancelled.getWorkCount());
         }
         // If this is a replacement, bring in the new version of the job
         if (incomingJob != null) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 8355e9c..44700c8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -471,7 +471,12 @@
                     job.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER),
                     mExecutionStartTimeElapsed - job.enqueueTime,
                     job.getJob().isUserInitiated(),
-                    job.shouldTreatAsUserInitiatedJob());
+                    job.shouldTreatAsUserInitiatedJob(),
+                    job.getJob().isPeriodic(),
+                    job.getJob().getMinLatencyMillis(),
+                    job.getEstimatedNetworkDownloadBytes(),
+                    job.getEstimatedNetworkUploadBytes(),
+                    job.getWorkCount());
             final String sourcePackage = job.getSourcePackageName();
             if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
                 final String componentPackage = job.getServiceComponent().getPackageName();
@@ -1435,9 +1440,14 @@
                 completedJob.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE),
                 completedJob.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY),
                 completedJob.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER),
-                0,
+                mExecutionStartTimeElapsed - completedJob.enqueueTime,
                 completedJob.getJob().isUserInitiated(),
-                completedJob.startedAsUserInitiatedJob);
+                completedJob.startedAsUserInitiatedJob,
+                completedJob.getJob().isPeriodic(),
+                completedJob.getJob().getMinLatencyMillis(),
+                completedJob.getEstimatedNetworkDownloadBytes(),
+                completedJob.getEstimatedNetworkUploadBytes(),
+                completedJob.getWorkCount());
         if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
             Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
                     getId());
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
index 5d2c926..b9e3b76 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
@@ -682,7 +682,7 @@
         static final String KEY_RESCHEDULED_JOB_DEADLINE_MS =
                 FC_CONFIG_PREFIX + "rescheduled_job_deadline_ms";
 
-        private static final boolean DEFAULT_FLEXIBILITY_ENABLED = true;
+        private static final boolean DEFAULT_FLEXIBILITY_ENABLED = false;
         @VisibleForTesting
         static final long DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS = 15 * MINUTE_IN_MILLIS;
         @VisibleForTesting
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 7cc2f28..6445c3b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -590,9 +590,10 @@
             this.sourceTag = tag;
         }
 
+        final String bnNamespace = namespace == null ? "" :  "@" + namespace + "@";
         this.batteryName = this.sourceTag != null
-                ? this.sourceTag + ":" + job.getService().getPackageName()
-                : job.getService().flattenToShortString();
+                ? bnNamespace + this.sourceTag + ":" + job.getService().getPackageName()
+                : bnNamespace + job.getService().flattenToShortString();
         this.tag = "*job*/" + this.batteryName + "#" + job.getId();
 
         this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index aca0a6e..175c8d1 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -35,7 +35,7 @@
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.AlarmManager;
-import android.app.IUidObserver;
+import android.app.UidObserver;
 import android.app.usage.UsageEvents;
 import android.app.usage.UsageStatsManagerInternal;
 import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
@@ -382,31 +382,11 @@
                 }
             };
 
-    private class QcUidObserver extends IUidObserver.Stub {
+    private class QcUidObserver extends UidObserver {
         @Override
         public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
             mHandler.obtainMessage(MSG_UID_PROCESS_STATE_CHANGED, uid, procState).sendToTarget();
         }
-
-        @Override
-        public void onUidGone(int uid, boolean disabled) {
-        }
-
-        @Override
-        public void onUidActive(int uid) {
-        }
-
-        @Override
-        public void onUidIdle(int uid, boolean disabled) {
-        }
-
-        @Override
-        public void onUidCachedChanged(int uid, boolean cached) {
-        }
-
-        @Override
-        public void onUidProcAdjChanged(int uid) {
-        }
     }
 
     /**
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java
index 3578c8a..58536675 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java
@@ -20,6 +20,7 @@
 import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.IUidObserver;
+import android.app.UidObserver;
 import android.os.RemoteException;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
@@ -61,7 +62,7 @@
     @GuardedBy("mLock")
     private final SparseIntArray mUidProcStateBucketCache = new SparseIntArray();
 
-    private final IUidObserver mUidObserver = new IUidObserver.Stub() {
+    private final IUidObserver mUidObserver = new UidObserver() {
         @Override
         public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
             final int newBucket = getProcStateBucket(procState);
@@ -85,22 +86,6 @@
                 notifyStateChangedLocked(uid);
             }
         }
-
-        @Override
-        public void onUidActive(int uid) {
-        }
-
-        @Override
-        public void onUidIdle(int uid, boolean disabled) {
-        }
-
-        @Override
-        public void onUidCachedChanged(int uid, boolean cached) {
-        }
-
-        @Override
-        public void onUidProcAdjChanged(int uid) {
-        }
     };
 
     ProcessStateModifier(@NonNull InternalResourceService irs) {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 1346d07..873234a 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -427,7 +427,7 @@
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void collapsePanels();
     method public void expandNotificationsPanel();
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public int getLastSystemKey();
-    method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void handleSystemKey(int);
+    method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void handleSystemKey(@NonNull android.view.KeyEvent);
     method public void sendNotificationFeedback(@Nullable String, @Nullable android.os.Bundle);
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setExpansionDisabledForSimNetworkLock(boolean);
     method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void togglePanel();
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 29f774c..a6313db 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -46,6 +46,7 @@
 import android.os.UserHandle;
 import android.util.Pair;
 import android.util.Slog;
+import android.view.KeyEvent;
 import android.view.View;
 
 import com.android.internal.statusbar.AppClipsServiceConnector;
@@ -740,7 +741,7 @@
      */
     @RequiresPermission(android.Manifest.permission.STATUS_BAR)
     @TestApi
-    public void handleSystemKey(int key) {
+    public void handleSystemKey(@NonNull KeyEvent key) {
         try {
             final IStatusBarService svc = getService();
             if (svc != null) {
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 12882a2..9efdf28 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -39,21 +39,22 @@
 import android.os.ResultReceiver;
 
 /**
- * Interface for a virtual device.
+ * Interface for a virtual device for communication between the system server and the process of
+ * the owner of the virtual device.
  *
  * @hide
  */
 interface IVirtualDevice {
 
     /**
-     * Returns the association ID for this virtual device.
+     * Returns the CDM association ID of this virtual device.
      *
      * @see AssociationInfo#getId()
      */
     int getAssociationId();
 
     /**
-     * Returns the unique device ID for this virtual device.
+     * Returns the unique ID of this virtual device.
      */
     int getDeviceId();
 
@@ -64,55 +65,99 @@
     void close();
 
     /**
-     * Notifies of an audio session being started.
+     * Notifies that an audio session being started.
      */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
-    void onAudioSessionStarting(
-            int displayId,
-            IAudioRoutingCallback routingCallback,
+    void onAudioSessionStarting(int displayId, IAudioRoutingCallback routingCallback,
             IAudioConfigChangedCallback configChangedCallback);
 
+    /**
+     * Notifies that an audio session has ended.
+     */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void onAudioSessionEnded();
 
+    /**
+     * Creates a new dpad and registers it with the input framework with the given token.
+     */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
-    void createVirtualDpad(
-            in VirtualDpadConfig config,
-            IBinder token);
+    void createVirtualDpad(in VirtualDpadConfig config, IBinder token);
+
+    /**
+     * Creates a new keyboard and registers it with the input framework with the given token.
+     */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
-    void createVirtualKeyboard(
-            in VirtualKeyboardConfig config,
-            IBinder token);
+    void createVirtualKeyboard(in VirtualKeyboardConfig config, IBinder token);
+
+    /**
+     * Creates a new mouse and registers it with the input framework with the given token.
+     */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
-    void createVirtualMouse(
-            in VirtualMouseConfig config,
-            IBinder token);
+    void createVirtualMouse(in VirtualMouseConfig config, IBinder token);
+
+    /**
+     * Creates a new touchscreen and registers it with the input framework with the given token.
+     */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
-    void createVirtualTouchscreen(
-            in VirtualTouchscreenConfig config,
-            IBinder token);
+    void createVirtualTouchscreen(in VirtualTouchscreenConfig config, IBinder token);
+
+    /**
+     * Creates a new navigation touchpad and registers it with the input framework with the given
+     * token.
+     */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
-    void createVirtualNavigationTouchpad(
-            in VirtualNavigationTouchpadConfig config,
-            IBinder token);
+    void createVirtualNavigationTouchpad(in VirtualNavigationTouchpadConfig config, IBinder token);
+
+    /**
+     * Removes the input device corresponding to the given token from the framework.
+     */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void unregisterInputDevice(IBinder token);
+
+    /**
+     * Returns the ID of the device corresponding to the given token, as registered with the input
+     * framework.
+     */
     int getInputDeviceId(IBinder token);
+
+    /**
+    * Injects a key event to the virtual dpad corresponding to the given token.
+    */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     boolean sendDpadKeyEvent(IBinder token, in VirtualKeyEvent event);
+
+    /**
+    * Injects a key event to the virtual keyboard corresponding to the given token.
+    */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     boolean sendKeyEvent(IBinder token, in VirtualKeyEvent event);
+
+    /**
+    * Injects a button event to the virtual mouse corresponding to the given token.
+    */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     boolean sendButtonEvent(IBinder token, in VirtualMouseButtonEvent event);
+
+    /**
+    * Injects a relative event to the virtual mouse corresponding to the given token.
+    */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     boolean sendRelativeEvent(IBinder token, in VirtualMouseRelativeEvent event);
+
+    /**
+    * Injects a scroll event to the virtual mouse corresponding to the given token.
+    */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     boolean sendScrollEvent(IBinder token, in VirtualMouseScrollEvent event);
+
+    /**
+    * Injects a touch event to the virtual touch input device corresponding to the given token.
+    */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     boolean sendTouchEvent(IBinder token, in VirtualTouchEvent event);
 
     /**
-     * Returns all virtual sensors for this device.
+     * Returns all virtual sensors created for this device.
      */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     List<VirtualSensor> getVirtualSensorList();
@@ -126,8 +171,13 @@
     /**
      * Launches a pending intent on the given display that is owned by this virtual device.
      */
-    void launchPendingIntent(
-            int displayId, in PendingIntent pendingIntent, in ResultReceiver resultReceiver);
+    void launchPendingIntent(int displayId, in PendingIntent pendingIntent,
+            in ResultReceiver resultReceiver);
+
+    /**
+     * Returns the current cursor position of the mouse corresponding to the given token, in x and y
+     * coordinates.
+     */
     PointF getCursorPosition(IBinder token);
 
     /** Sets whether to show or hide the cursor while this virtual device is active. */
@@ -140,8 +190,12 @@
      * intent.
      */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
-    void registerIntentInterceptor(
-            in IVirtualDeviceIntentInterceptor intentInterceptor, in IntentFilter filter);
+    void registerIntentInterceptor(in IVirtualDeviceIntentInterceptor intentInterceptor,
+            in IntentFilter filter);
+
+    /**
+     * Unregisters a previously registered intent interceptor.
+     */
     @EnforcePermission("CREATE_VIRTUAL_DEVICE")
     void unregisterIntentInterceptor(in IVirtualDeviceIntentInterceptor intentInterceptor);
 }
diff --git a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
index 4f49b8d..07743cef5 100644
--- a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
@@ -101,7 +101,7 @@
      *
      * @param deviceId id of the virtual device.
      * @param sound effect type corresponding to
-     *     {@code android.media.AudioManager.SystemSoundEffect}
+     *   {@code android.media.AudioManager.SystemSoundEffect}
      */
     void playSoundEffect(int deviceId, int effectType);
 }
diff --git a/core/java/android/companion/virtual/IVirtualDeviceSoundEffectListener.aidl b/core/java/android/companion/virtual/IVirtualDeviceSoundEffectListener.aidl
index 91c209f..f284554 100644
--- a/core/java/android/companion/virtual/IVirtualDeviceSoundEffectListener.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceSoundEffectListener.aidl
@@ -28,7 +28,7 @@
      * Called when there's sound effect to be played on Virtual Device.
      *
      * @param sound effect type corresponding to
-     *     {@code android.media.AudioManager.SystemSoundEffect}
+     *   {@code android.media.AudioManager.SystemSoundEffect}
      */
     void onPlaySoundEffect(int effectType);
 }
diff --git a/core/java/android/companion/virtual/VirtualDevice.java b/core/java/android/companion/virtual/VirtualDevice.java
index 4a09186..4ee65e0 100644
--- a/core/java/android/companion/virtual/VirtualDevice.java
+++ b/core/java/android/companion/virtual/VirtualDevice.java
@@ -26,6 +26,11 @@
 
 /**
  * Details of a particular virtual device.
+ *
+ * <p>Read-only device representation exposing the properties of an existing virtual device.
+ *
+ * <p class="note">Not to be confused with {@link VirtualDeviceManager.VirtualDevice}, which is used
+ * by the virtual device creator and allows them to manage the device.
  */
 public final class VirtualDevice implements Parcelable {
 
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index d3d635e..da6784b 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -68,7 +68,13 @@
 import java.util.function.IntConsumer;
 
 /**
- * System level service for managing virtual devices.
+ * System level service for creation and management of virtual devices.
+ *
+ * <p>VirtualDeviceManager enables interactive sharing of capabilities between the host Android
+ * device and a remote device.
+ *
+ * <p class="note">Not to be confused with the Android Studio's Virtual Device Manager, which allows
+ * for device emulation.
  */
 @SystemService(Context.VIRTUAL_DEVICE_SERVICE)
 public final class VirtualDeviceManager {
@@ -174,6 +180,9 @@
 
     /**
      * Returns the details of all available virtual devices.
+     *
+     * <p>The returned objects are read-only representations that expose the properties of all
+     * existing virtual devices.
      */
     @NonNull
     public List<android.companion.virtual.VirtualDevice> getVirtualDevices() {
@@ -252,11 +261,12 @@
      *
      * @param deviceId - id of the virtual audio device
      * @return Device specific session id to be used for audio playback (see
-     *     {@link android.media.AudioManager.generateAudioSessionId}) if virtual device has
-     *     {@link VirtualDeviceParams.POLICY_TYPE_AUDIO} set to
-     *     {@link VirtualDeviceParams.DEVICE_POLICY_CUSTOM} and Virtual Audio Device
-     *     is configured in context-aware mode.
-     *     Otherwise {@link AUDIO_SESSION_ID_GENERATE} constant is returned.
+     *   {@link AudioManager#generateAudioSessionId}) if virtual device has
+     *   {@link VirtualDeviceParams#POLICY_TYPE_AUDIO} set to
+     *   {@link VirtualDeviceParams#DEVICE_POLICY_CUSTOM} and Virtual Audio Device
+     *   is configured in context-aware mode. Otherwise
+     *   {@link AudioManager#AUDIO_SESSION_ID_GENERATE} constant is returned.
+     *
      * @hide
      */
     public int getAudioPlaybackSessionId(int deviceId) {
@@ -275,11 +285,12 @@
      *
      * @param deviceId - id of the virtual audio device
      * @return Device specific session id to be used for audio recording (see
-     *     {@link android.media.AudioManager.generateAudioSessionId}) if virtual device has
-     *     {@link VirtualDeviceParams.POLICY_TYPE_AUDIO} set to
-     *     {@link VirtualDeviceParams.DEVICE_POLICY_CUSTOM} and Virtual Audio Device
-     *     is configured in context-aware mode.
-     *     Otherwise {@link AUDIO_SESSION_ID_GENERATE} constant is returned.
+     *   {@link AudioManager#generateAudioSessionId}) if virtual device has
+     *   {@link VirtualDeviceParams#POLICY_TYPE_AUDIO} set to
+     *   {@link VirtualDeviceParams#DEVICE_POLICY_CUSTOM} and Virtual Audio Device
+     *   is configured in context-aware mode. Otherwise
+     *   {@link AudioManager#AUDIO_SESSION_ID_GENERATE} constant is returned.
+     *
      * @hide
      */
     public int getAudioRecordingSessionId(int deviceId) {
@@ -296,10 +307,11 @@
     /**
      * Requests sound effect to be played on virtual device.
      *
-     * @see android.media.AudioManager#playSoundEffect(int)
+     * @see AudioManager#playSoundEffect(int)
      *
      * @param deviceId - id of the virtual audio device
      * @param effectType the type of sound effect
+     *
      * @hide
      */
     public void playSoundEffect(int deviceId, @AudioManager.SystemSoundEffect int effectType) {
@@ -315,11 +327,18 @@
     }
 
     /**
-     * A virtual device has its own virtual display, audio output, microphone, sensors, etc. The
-     * creator of a virtual device can take the output from the virtual display and stream it over
-     * to another device, and inject input events that are received from the remote device.
+     * A representation of a virtual device.
      *
-     * TODO(b/204081582): Consider using a builder pattern for the input APIs.
+     * <p>A virtual device can have its own virtual displays, audio input/output, sensors, etc.
+     * The creator of a virtual device can take the output from the virtual display and stream it
+     * over to another device, and inject input and sensor events that are received from the remote
+     * device.
+     *
+     * <p>This object is only used by the virtual device creator and allows them to manage the
+     * device's behavior, peripherals, and the user interaction with that device.
+     *
+     * <p class="note">Not to be confused with {@link android.companion.virtual.VirtualDevice},
+     * which is a read-only representation exposing the properties of an existing virtual device.
      *
      * @hide
      */
@@ -346,8 +365,10 @@
         }
 
         /**
-         * @return A new Context bound to this device. This is a convenience method equivalent to
-         * calling {@link Context#createDeviceContext(int)} with the device id of this device.
+         * Returns a new context bound to this device.
+         *
+         * <p>This is a convenience method equivalent to calling
+         * {@link Context#createDeviceContext(int)} with the id of this device.
          */
         public @NonNull Context createContext() {
             return mVirtualDeviceInternal.createContext();
@@ -400,20 +421,19 @@
          * @param height The height of the virtual display in pixels, must be greater than 0.
          * @param densityDpi The density of the virtual display in dpi, must be greater than 0.
          * @param surface The surface to which the content of the virtual display should
-         * be rendered, or null if there is none initially. The surface can also be set later using
-         * {@link VirtualDisplay#setSurface(Surface)}.
+         *   be rendered, or null if there is none initially. The surface can also be set later
+         *   using {@link VirtualDisplay#setSurface(Surface)}.
          * @param flags A combination of virtual display flags accepted by
-         * {@link DisplayManager#createVirtualDisplay}. In addition, the following flags are
-         * automatically set for all virtual devices:
-         * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC VIRTUAL_DISPLAY_FLAG_PUBLIC} and
-         * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
-         * VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}.
+         *   {@link DisplayManager#createVirtualDisplay}. In addition, the following flags are
+         *   automatically set for all virtual devices:
+         *   {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC} and
+         *   {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}.
          * @param executor The executor on which {@code callback} will be invoked. This is ignored
-         * if {@code callback} is {@code null}. If {@code callback} is specified, this executor must
-         * not be null.
+         *   if {@code callback} is {@code null}. If {@code callback} is specified, this executor
+         *   must not be null.
          * @param callback Callback to call when the state of the {@link VirtualDisplay} changes
          * @return The newly created virtual display, or {@code null} if the application could
-         * not create the virtual display.
+         *   not create the virtual display.
          *
          * @see DisplayManager#createVirtualDisplay
          *
@@ -450,11 +470,11 @@
          *
          * @param config The configuration of the display.
          * @param executor The executor on which {@code callback} will be invoked. This is ignored
-         * if {@code callback} is {@code null}. If {@code callback} is specified, this executor must
-         * not be null.
+         *   if {@code callback} is {@code null}. If {@code callback} is specified, this executor
+         *   must not be null.
          * @param callback Callback to call when the state of the {@link VirtualDisplay} changes
          * @return The newly created virtual display, or {@code null} if the application could
-         * not create the virtual display.
+         *   not create the virtual display.
          *
          * @see DisplayManager#createVirtualDisplay
          */
@@ -478,7 +498,7 @@
         /**
          * Creates a virtual dpad.
          *
-         * @param config the configurations of the virtual Dpad.
+         * @param config the configurations of the virtual dpad.
          */
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
@@ -500,11 +520,10 @@
         /**
          * Creates a virtual keyboard.
          *
-         * @param display         the display that the events inputted through this device should
-         *                        target
-         * @param inputDeviceName the name to call this input device
-         * @param vendorId        the PCI vendor id
-         * @param productId       the product id, as defined by the vendor
+         * @param display the display that the events inputted through this device should target.
+         * @param inputDeviceName the name of this keyboard device.
+         * @param vendorId the PCI vendor id.
+         * @param productId the product id, as defined by the vendor.
          * @see #createVirtualKeyboard(VirtualKeyboardConfig config)
          * @deprecated Use {@link #createVirtualKeyboard(VirtualKeyboardConfig config)} instead
          */
@@ -537,14 +556,12 @@
         /**
          * Creates a virtual mouse.
          *
-         * @param display         the display that the events inputted through this device should
-         *                        target
-         * @param inputDeviceName the name to call this input device
-         * @param vendorId        the PCI vendor id
-         * @param productId       the product id, as defined by the vendor
+         * @param display the display that the events inputted through this device should target.
+         * @param inputDeviceName the name of this mouse.
+         * @param vendorId the PCI vendor id.
+         * @param productId the product id, as defined by the vendor.
          * @see #createVirtualMouse(VirtualMouseConfig config)
          * @deprecated Use {@link #createVirtualMouse(VirtualMouseConfig config)} instead
-         * *
          */
         @Deprecated
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@@ -576,11 +593,10 @@
         /**
          * Creates a virtual touchscreen.
          *
-         * @param display         the display that the events inputted through this device should
-         *                        target
-         * @param inputDeviceName the name to call this input device
-         * @param vendorId        the PCI vendor id
-         * @param productId       the product id, as defined by the vendor
+         * @param display the display that the events inputted through this device should target.
+         * @param inputDeviceName the name of this touchscreen device.
+         * @param vendorId the PCI vendor id.
+         * @param productId the product id, as defined by the vendor.
          * @see #createVirtualTouchscreen(VirtualTouchscreenConfig config)
          * @deprecated Use {@link #createVirtualTouchscreen(VirtualTouchscreenConfig config)}
          * instead
@@ -605,11 +621,13 @@
         /**
          * Creates a virtual touchpad in navigation mode.
          *
-         * A touchpad in navigation mode means that its events are interpreted as navigation events
-         * (up, down, etc) instead of using them to update a cursor's absolute position. If the
-         * events are not consumed they are converted to DPAD events.
+         * <p>A touchpad in navigation mode means that its events are interpreted as navigation
+         * events (up, down, etc) instead of using them to update a cursor's absolute position. If
+         * the events are not consumed they are converted to DPAD events and delivered to the target
+         * again.
          *
          * @param config the configurations of the virtual navigation touchpad.
+         * @see android.view.InputDevice#SOURCE_TOUCH_NAVIGATION
          */
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
@@ -629,10 +647,10 @@
          *
          * @param display The target virtual display to capture from and inject into.
          * @param executor The {@link Executor} object for the thread on which to execute
-         *                the callback. If <code>null</code>, the {@link Executor} associated with
-         *                the main {@link Looper} will be used.
+         *   the callback. If <code>null</code>, the {@link Executor} associated with the main
+         *   {@link Looper} will be used.
          * @param callback Interface to be notified when playback or recording configuration of
-         *                applications running on virtual display is changed.
+         *   applications running on virtual display is changed.
          * @return A {@link VirtualAudioDevice} instance.
          */
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@@ -648,7 +666,7 @@
          * Sets the visibility of the pointer icon for this VirtualDevice's associated displays.
          *
          * @param showPointerIcon True if the pointer should be shown; false otherwise. The default
-         *                        visibility is true.
+         *   visibility is true.
          */
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
         @NonNull
@@ -670,8 +688,7 @@
         }
 
         /**
-         * Removes an activity listener previously added with
-         * {@link #addActivityListener}.
+         * Removes an activity listener previously added with {@link #addActivityListener}.
          *
          * @param listener The listener to remove.
          * @see #addActivityListener(Executor, ActivityListener)
@@ -693,10 +710,10 @@
         }
 
         /**
-         * Removes a sound effect listener previously added with {@link #addActivityListener}.
+         * Removes a sound effect listener previously added with {@link #addSoundEffectListener}.
          *
          * @param soundEffectListener The listener to remove.
-         * @see #addActivityListener(Executor, ActivityListener)
+         * @see #addSoundEffectListener(Executor, SoundEffectListener)
          */
         public void removeSoundEffectListener(@NonNull SoundEffectListener soundEffectListener) {
             mVirtualDeviceInternal.removeSoundEffectListener(soundEffectListener);
@@ -723,7 +740,7 @@
         }
 
         /**
-         * Unregisters the intent interceptorCallback previously registered with
+         * Unregisters the intent interceptor previously registered with
          * {@link #registerIntentInterceptor}.
          */
         @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@@ -761,9 +778,9 @@
          * {@link #onDisplayEmpty(int)} will be called. If the value topActivity is cached, it
          * should be cleared when {@link #onDisplayEmpty(int)} is called.
          *
-         * @param displayId   The display ID on which the activity change happened.
+         * @param displayId The display ID on which the activity change happened.
          * @param topActivity The component name of the top activity.
-         * @param userId      The user ID associated with the top activity.
+         * @param userId The user ID associated with the top activity.
          */
         default void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity,
                 @UserIdInt int userId) {}
@@ -800,6 +817,7 @@
 
     /**
      * Listener for system sound effect playback on virtual device.
+     *
      * @hide
      */
     @SystemApi
@@ -808,8 +826,8 @@
         /**
          * Called when there's a system sound effect to be played on virtual device.
          *
-         * @param effectType - system sound effect type, see
-         *     {@code android.media.AudioManager.SystemSoundEffect}
+         * @param effectType - system sound effect type
+         * @see android.media.AudioManager.SystemSoundEffect
          */
         void onPlaySoundEffect(@AudioManager.SystemSoundEffect int effectType);
     }
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 9a34dbe..45d6dc6 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -34,6 +34,7 @@
 import android.companion.virtual.sensor.VirtualSensorConfig;
 import android.companion.virtual.sensor.VirtualSensorDirectChannelCallback;
 import android.content.ComponentName;
+import android.content.Context;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SharedMemory;
@@ -680,7 +681,7 @@
          * {@link #NAVIGATION_POLICY_DEFAULT_ALLOWED}, meaning activities are allowed to launch
          * unless they are in {@code blockedCrossTaskNavigations}.
          *
-         * <p> This method must not be called if {@link #setAllowedCrossTaskNavigations(Set)} has
+         * <p>This method must not be called if {@link #setAllowedCrossTaskNavigations(Set)} has
          * been called.
          *
          * @throws IllegalArgumentException if {@link #setAllowedCrossTaskNavigations(Set)} has
@@ -847,11 +848,11 @@
          * <p>Requires {@link #DEVICE_POLICY_CUSTOM} to be set for {@link #POLICY_TYPE_AUDIO},
          * otherwise {@link #build()} method will throw {@link IllegalArgumentException} if
          * the playback session id is set to value other than
-         * {@link android.media.AudioManager.AUDIO_SESSION_ID_GENERATE}.
+         * {@link android.media.AudioManager#AUDIO_SESSION_ID_GENERATE}.
          *
          * @param playbackSessionId requested device-specific audio session id for playback
-         * @see android.media.AudioManager.generateAudioSessionId()
-         * @see android.media.AudioTrack.Builder.setContext(Context)
+         * @see android.media.AudioManager#generateAudioSessionId()
+         * @see android.media.AudioTrack.Builder#setContext(Context)
          */
         @NonNull
         public Builder setAudioPlaybackSessionId(int playbackSessionId) {
@@ -871,11 +872,11 @@
          * <p>Requires {@link #DEVICE_POLICY_CUSTOM} to be set for {@link #POLICY_TYPE_AUDIO},
          * otherwise {@link #build()} method will throw {@link IllegalArgumentException} if
          * the recording session id is set to value other than
-         * {@link android.media.AudioManager.AUDIO_SESSION_ID_GENERATE}.
+         * {@link android.media.AudioManager#AUDIO_SESSION_ID_GENERATE}.
          *
          * @param recordingSessionId requested device-specific audio session id for playback
-         * @see android.media.AudioManager.generateAudioSessionId()
-         * @see android.media.AudioRecord.Builder.setContext(Context)
+         * @see android.media.AudioManager#generateAudioSessionId()
+         * @see android.media.AudioRecord.Builder#setContext(Context)
          */
         @NonNull
         public Builder setAudioRecordingSessionId(int recordingSessionId) {
diff --git a/core/java/android/companion/virtual/audio/AudioCapture.java b/core/java/android/companion/virtual/audio/AudioCapture.java
index d6d0d2b..dd5e660 100644
--- a/core/java/android/companion/virtual/audio/AudioCapture.java
+++ b/core/java/android/companion/virtual/audio/AudioCapture.java
@@ -56,12 +56,12 @@
 
     /**
      * Sets the {@link AudioRecord} to handle audio capturing.
-     * Callers may call this multiple times with different audio records to change
-     * the underlying {@link AudioRecord} without stopping and re-starting recording.
      *
-     * @param audioRecord The underlying {@link AudioRecord} to use for capture,
-     * or null if no audio (i.e. silence) should be captured while still keeping the
-     * record in a recording state.
+     * <p>Callers may call this multiple times with different audio records to change the underlying
+     * {@link AudioRecord} without stopping and re-starting recording.
+     *
+     * @param audioRecord The underlying {@link AudioRecord} to use for capture, or null if no audio
+     *   (i.e. silence) should be captured while still keeping the record in a recording state.
      */
     void setAudioRecord(@Nullable AudioRecord audioRecord) {
         Log.d(TAG, "set AudioRecord with " + audioRecord);
diff --git a/core/java/android/companion/virtual/audio/AudioInjection.java b/core/java/android/companion/virtual/audio/AudioInjection.java
index 9d6a3eb..5de5f7e 100644
--- a/core/java/android/companion/virtual/audio/AudioInjection.java
+++ b/core/java/android/companion/virtual/audio/AudioInjection.java
@@ -65,12 +65,12 @@
 
     /**
      * Sets the {@link AudioTrack} to handle audio injection.
-     * Callers may call this multiple times with different audio tracks to change
-     * the underlying {@link AudioTrack} without stopping and re-starting injection.
      *
-     * @param audioTrack The underlying {@link AudioTrack} to use for injection,
-     * or null if no audio (i.e. silence) should be injected while still keeping the
-     * record in a playing state.
+     * <p>Callers may call this multiple times with different audio tracks to change the underlying
+     * {@link AudioTrack} without stopping and re-starting injection.
+     *
+     * @param audioTrack The underlying {@link AudioTrack} to use for injection, or null if no audio
+     *   (i.e. silence) should be injected while still keeping the record in a playing state.
      */
     void setAudioTrack(@Nullable AudioTrack audioTrack) {
         Log.d(TAG, "set AudioTrack with " + audioTrack);
diff --git a/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl b/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl
index 3cb0572..dcdb6c6 100644
--- a/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl
+++ b/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl
@@ -33,7 +33,7 @@
      * @param enabled Whether the sensor is enabled.
      * @param samplingPeriodMicros The requested sensor's sampling period in microseconds.
      * @param batchReportingLatencyMicros The requested maximum time interval in microseconds
-     * between the delivery of two batches of sensor events.
+     *   between the delivery of two batches of sensor events.
      */
     void onConfigurationChanged(in VirtualSensor sensor, boolean enabled, int samplingPeriodMicros,
             int batchReportLatencyMicros);
@@ -60,7 +60,7 @@
      * @param sensor The sensor, for which the channel was configured.
      * @param rateLevel The rate level used to configure the direct sensor channel.
      * @param reportToken A positive sensor report token, used to differentiate between events from
-     * different sensors within the same channel.
+     *   different sensors within the same channel.
      */
     void onDirectChannelConfigured(int channelHandle, in VirtualSensor sensor, int rateLevel,
             int reportToken);
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensor.java b/core/java/android/companion/virtual/sensor/VirtualSensor.java
index bda44d4..eaa1792 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensor.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensor.java
@@ -30,7 +30,7 @@
  * Representation of a sensor on a remote device, capable of sending events, such as an
  * accelerometer or a gyroscope.
  *
- * This registers the sensor device with the sensor framework as a runtime sensor.
+ * <p>A virtual sensor device is registered with the sensor framework as a runtime sensor.
  *
  * @hide
  */
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java b/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java
index e6bd6da..4d586f6 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java
@@ -45,10 +45,10 @@
      *
      * @param sensor The sensor whose requested injection parameters have changed.
      * @param enabled Whether the sensor is enabled. True if any listeners are currently registered,
-     * and false otherwise.
+     *   and false otherwise.
      * @param samplingPeriod The requested sampling period of the sensor.
      * @param batchReportLatency The requested maximum time interval between the delivery of two
-     * batches of sensor events.
+     *   batches of sensor events.
      */
     void onConfigurationChanged(@NonNull VirtualSensor sensor, boolean enabled,
             @NonNull Duration samplingPeriod, @NonNull Duration batchReportLatency);
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
index ef55ca9..3bdf9aa 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
@@ -31,7 +31,9 @@
 
 /**
  * Configuration for creation of a virtual sensor.
+ *
  * @see VirtualSensor
+ *
  * @hide
  */
 @SystemApi
@@ -122,6 +124,7 @@
 
     /**
      * Returns the vendor string of the sensor.
+     *
      * @see Builder#setVendor
      */
     @Nullable
@@ -130,7 +133,8 @@
     }
 
     /**
-     * Returns maximum range of the sensor in the sensor's unit.
+     * Returns the maximum range of the sensor in the sensor's unit.
+     *
      * @see Sensor#getMaximumRange
      */
     public float getMaximumRange() {
@@ -138,7 +142,8 @@
     }
 
     /**
-     * Returns The resolution of the sensor in the sensor's unit.
+     * Returns the resolution of the sensor in the sensor's unit.
+     *
      * @see Sensor#getResolution
      */
     public float getResolution() {
@@ -146,7 +151,8 @@
     }
 
     /**
-     * Returns The power in mA used by this sensor while in use.
+     * Returns the power in mA used by this sensor while in use.
+     *
      * @see Sensor#getPower
      */
     public float getPower() {
@@ -154,8 +160,9 @@
     }
 
     /**
-     * Returns The minimum delay allowed between two events in microseconds, or zero depending on
+     * Returns the minimum delay allowed between two events in microseconds, or zero depending on
      * the sensor type.
+     *
      * @see Sensor#getMinDelay
      */
     public int getMinDelay() {
@@ -163,7 +170,8 @@
     }
 
     /**
-     * Returns The maximum delay between two sensor events in microseconds.
+     * Returns the maximum delay between two sensor events in microseconds.
+     *
      * @see Sensor#getMaxDelay
      */
     public int getMaxDelay() {
@@ -201,6 +209,7 @@
 
     /**
      * Returns the sensor flags.
+     *
      * @hide
      */
     public int getFlags() {
@@ -233,7 +242,7 @@
          *
          * @param type The type of the sensor, matching {@link Sensor#getType}.
          * @param name The name of the sensor. Must be unique among all sensors with the same type
-         *             that belong to the same virtual device.
+         *   that belong to the same virtual device.
          */
         public Builder(@IntRange(from = 1) int type, @NonNull String name) {
             if (type <= 0) {
@@ -275,6 +284,7 @@
 
         /**
          * Sets the maximum range of the sensor in the sensor's unit.
+         *
          * @see Sensor#getMaximumRange
          */
         @NonNull
@@ -285,6 +295,7 @@
 
         /**
          * Sets the resolution of the sensor in the sensor's unit.
+         *
          * @see Sensor#getResolution
          */
         @NonNull
@@ -295,6 +306,7 @@
 
         /**
          * Sets the power in mA used by this sensor while in use.
+         *
          * @see Sensor#getPower
          */
         @NonNull
@@ -305,6 +317,7 @@
 
         /**
          * Sets the minimum delay allowed between two events in microseconds.
+         *
          * @see Sensor#getMinDelay
          */
         @NonNull
@@ -315,6 +328,7 @@
 
         /**
          * Sets the maximum delay between two sensor events in microseconds.
+         *
          * @see Sensor#getMaxDelay
          */
         @NonNull
@@ -339,11 +353,11 @@
          * Sets whether direct sensor channel of the given types is supported.
          *
          * @param memoryTypes A combination of {@link SensorDirectChannel.MemoryType} flags
-         * indicating the types of shared memory supported for creating direct channels. Only
-         * {@link SensorDirectChannel#TYPE_MEMORY_FILE} direct channels may be supported for virtual
-         * sensors.
+         *   indicating the types of shared memory supported for creating direct channels. Only
+         *   {@link SensorDirectChannel#TYPE_MEMORY_FILE} direct channels may be supported for
+         *   virtual sensors.
          * @throws IllegalArgumentException if {@link SensorDirectChannel#TYPE_HARDWARE_BUFFER} is
-         * set to be supported.
+         *   set to be supported.
          */
         @NonNull
         public VirtualSensorConfig.Builder setDirectChannelTypesSupported(
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelCallback.java b/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelCallback.java
index d352f94f..f10e9d0 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelCallback.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelCallback.java
@@ -45,6 +45,8 @@
  * <p>The callback is tied to the VirtualDevice's lifetime as the virtual sensors are created when
  * the device is created and destroyed when the device is destroyed.
  *
+ * @see VirtualSensorDirectChannelWriter
+ *
  * @hide
  */
 @SystemApi
@@ -94,7 +96,7 @@
      * @param sensor The sensor, for which the channel was configured.
      * @param rateLevel The rate level used to configure the direct sensor channel.
      * @param reportToken A positive sensor report token, used to differentiate between events from
-     * different sensors within the same channel.
+     *   different sensors within the same channel.
      *
      * @see VirtualSensorConfig.Builder#setHighestDirectReportRateLevel(int)
      * @see VirtualSensorConfig.Builder#setDirectChannelTypesSupported(int)
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java b/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java
index 6aed96f..bf78dd0 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java
@@ -41,6 +41,41 @@
  * write the events from the relevant sensors directly to the shared memory regions of the
  * corresponding {@link SensorDirectChannel} instances.
  *
+ * <p>Example:
+ * <p>During sensor and virtual device creation:
+ * <pre>
+ * VirtualSensorDirectChannelWriter writer = new VirtualSensorDirectChannelWriter();
+ * VirtualSensorDirectChannelCallback callback = new VirtualSensorDirectChannelCallback() {
+ *     @Override
+ *     public void onDirectChannelCreated(int channelHandle, SharedMemory sharedMemory) {
+ *         writer.addChannel(channelHandle, sharedMemory);
+ *     }
+ *     @Override
+ *     public void onDirectChannelDestroyed(int channelHandle);
+ *         writer.removeChannel(channelHandle);
+ *     }
+ *     @Override
+ *     public void onDirectChannelConfigured(int channelHandle, VirtualSensor sensor, int rateLevel,
+ *             int reportToken)
+ *         if (!writer.configureChannel(channelHandle, sensor, rateLevel, reportToken)) {
+ *              // handle error
+ *         }
+ *     }
+ * }
+ * </pre>
+ * <p>During the virtual device lifetime:
+ * <pre>
+ * VirtualSensor sensor = ...
+ * while (shouldInjectEvents(sensor)) {
+ *     if (!writer.writeSensorEvent(sensor, event)) {
+ *         // handle error
+ *     }
+ * }
+ * writer.close();
+ * </pre>
+ * <p>Note that the virtual device owner should take the currently configured rate level into
+ * account when deciding whether and how often to inject events for a particular sensor.
+ *
  * @see android.hardware.SensorDirectChannel#configure
  * @see VirtualSensorDirectChannelCallback
  *
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorEvent.java b/core/java/android/companion/virtual/sensor/VirtualSensorEvent.java
index 01b4975..a368467e 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorEvent.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorEvent.java
@@ -121,7 +121,7 @@
          * monotonically increasing using the same time base as
          * {@link android.os.SystemClock#elapsedRealtimeNanos()}.
          *
-         * If not explicitly set, the current timestamp is used for the sensor event.
+         * <p>If not explicitly set, the current timestamp is used for the sensor event.
          *
          * @see android.hardware.SensorEvent#timestamp
          */
diff --git a/core/java/android/util/TeeWriter.java b/core/java/android/util/TeeWriter.java
new file mode 100644
index 0000000..439a0c2
--- /dev/null
+++ b/core/java/android/util/TeeWriter.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import android.annotation.NonNull;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Objects;
+
+/**
+ * Writer that offers to "tee" identical output to multiple underlying
+ * {@link Writer} instances.
+ *
+ * @see https://man7.org/linux/man-pages/man1/tee.1.html
+ * @hide
+ */
+public class TeeWriter extends Writer {
+    private final @NonNull Writer[] mWriters;
+
+    public TeeWriter(@NonNull Writer... writers) {
+        for (Writer writer : writers) {
+            Objects.requireNonNull(writer);
+        }
+        mWriters = writers;
+    }
+
+    @Override
+    public void write(char[] cbuf, int off, int len) throws IOException {
+        for (Writer writer : mWriters) {
+            writer.write(cbuf, off, len);
+        }
+    }
+
+    @Override
+    public void flush() throws IOException {
+        for (Writer writer : mWriters) {
+            writer.flush();
+        }
+    }
+
+    @Override
+    public void close() throws IOException {
+        for (Writer writer : mWriters) {
+            writer.close();
+        }
+    }
+}
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 24a0355..d35aff9 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -158,6 +158,14 @@
      */
     public Matrix transform;
 
+    /**
+     * The input token for the window to which focus should be transferred when this input window
+     * can be successfully focused. If null, this input window will not transfer its focus to
+     * any other window.
+     */
+    @Nullable
+    public IBinder focusTransferTarget;
+
     private native void nativeDispose();
 
     public InputWindowHandle(InputApplicationHandle inputApplicationHandle, int displayId) {
@@ -195,6 +203,7 @@
             transform = new Matrix();
             transform.set(other.transform);
         }
+        focusTransferTarget = other.focusTransferTarget;
     }
 
     @Override
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 0db52aa..bc6a3b5 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -265,7 +265,7 @@
             int transformHint);
     private static native void nativeRemoveCurrentInputFocus(long nativeObject, int displayId);
     private static native void nativeSetFocusedWindow(long transactionObj, IBinder toToken,
-            String windowName, IBinder focusedToken, String focusedWindowName, int displayId);
+            String windowName, int displayId);
     private static native void nativeSetFrameTimelineVsync(long transactionObj,
             long frameTimelineVsyncId);
     private static native void nativeAddJankDataListener(long nativeListener,
@@ -3604,28 +3604,7 @@
          */
         public Transaction setFocusedWindow(@NonNull IBinder token, String windowName,
                 int displayId) {
-            nativeSetFocusedWindow(mNativeObject, token,  windowName,
-                    null /* focusedToken */, null /* focusedWindowName */, displayId);
-            return this;
-        }
-
-        /**
-         * Set focus on the window identified by the input {@code token} if the window identified by
-         * the input {@code focusedToken} is currently focused. If the {@code focusedToken} does not
-         * have focus, the request is dropped.
-         *
-         * This is used by forward focus transfer requests from clients that host embedded windows,
-         * and want to transfer focus to/from them.
-         *
-         * @hide
-         */
-        public Transaction requestFocusTransfer(@NonNull IBinder token,
-                                                String windowName,
-                                                @NonNull IBinder focusedToken,
-                                                String focusedWindowName,
-                                                int displayId) {
-            nativeSetFocusedWindow(mNativeObject, token, windowName, focusedToken,
-                    focusedWindowName, displayId);
+            nativeSetFocusedWindow(mNativeObject, token, windowName, displayId);
             return this;
         }
 
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 0560caf..9868144 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -57,18 +57,16 @@
         SurfaceControl mLeash;
         Rect mFrame;
         Rect mAttachedFrame;
+        IBinder mFocusGrantToken;
 
-        State(SurfaceControl sc, WindowManager.LayoutParams p,
-                int displayId, IBinder inputChannelToken, IWindow client, SurfaceControl leash,
-                Rect frame, Rect attachedFrame) {
+        State(SurfaceControl sc, WindowManager.LayoutParams p, int displayId, IWindow client,
+                SurfaceControl leash, Rect frame) {
             mSurfaceControl = sc;
             mParams.copyFrom(p);
             mDisplayId = displayId;
-            mInputChannelToken = inputChannelToken;
             mClient = client;
             mLeash = leash;
             mFrame = frame;
-            mAttachedFrame = attachedFrame;
         }
     };
 
@@ -182,45 +180,53 @@
                 .setParent(leash)
                 .build();
 
+        final State state = new State(sc, attrs, displayId, window, leash, /* frame= */ new Rect());
+        synchronized (this) {
+            State parentState = mStateForWindow.get(attrs.token);
+            if (parentState != null) {
+                state.mAttachedFrame = parentState.mFrame;
+            }
+
+            // Give the first window the mFocusGrantToken since that's the token the host can use
+            // to give focus to the embedded.
+            if (mStateForWindow.isEmpty()) {
+                state.mFocusGrantToken = mFocusGrantToken;
+            } else {
+                state.mFocusGrantToken = new Binder();
+            }
+
+            mStateForWindow.put(window.asBinder(), state);
+        }
+
+        if (state.mAttachedFrame == null) {
+            outAttachedFrame.set(0, 0, -1, -1);
+        } else {
+            outAttachedFrame.set(state.mAttachedFrame);
+        }
+        outSizeCompatScale[0] = 1f;
+
         if (((attrs.inputFeatures &
                 WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0)) {
             try {
                 if (mRealWm instanceof IWindowSession.Stub) {
                     mRealWm.grantInputChannel(displayId,
                             new SurfaceControl(sc, "WindowlessWindowManager.addToDisplay"),
-                            window, mHostInputToken,
-                            attrs.flags, attrs.privateFlags, attrs.inputFeatures, attrs.type,
-                            attrs.token, mFocusGrantToken, attrs.getTitle().toString(),
+                            window, mHostInputToken, attrs.flags, attrs.privateFlags,
+                            attrs.inputFeatures, attrs.type,
+                            attrs.token, state.mFocusGrantToken, attrs.getTitle().toString(),
                             outInputChannel);
                 } else {
                     mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags,
                             attrs.privateFlags, attrs.inputFeatures, attrs.type, attrs.token,
-                            mFocusGrantToken, attrs.getTitle().toString(), outInputChannel);
+                            state.mFocusGrantToken, attrs.getTitle().toString(), outInputChannel);
                 }
+                state.mInputChannelToken =
+                        outInputChannel != null ? outInputChannel.getToken() : null;
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to grant input to surface: ", e);
             }
         }
 
-        final State state = new State(sc, attrs, displayId,
-                outInputChannel != null ? outInputChannel.getToken() : null, window,
-                leash, /* frame= */ new Rect(), /* attachedFrame= */ null);
-        Rect parentFrame = null;
-        synchronized (this) {
-            State parentState = mStateForWindow.get(attrs.token);
-            if (parentState != null) {
-                parentFrame = parentState.mFrame;
-            }
-            mStateForWindow.put(window.asBinder(), state);
-        }
-        state.mAttachedFrame = parentFrame;
-        if (parentFrame == null) {
-            outAttachedFrame.set(0, 0, -1, -1);
-        } else {
-            outAttachedFrame.set(parentFrame);
-        }
-        outSizeCompatScale[0] = 1f;
-
         final int res = WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE |
                         WindowManagerGlobal.ADD_FLAG_USE_BLAST;
 
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 6872536..1840bcb 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -1217,9 +1217,11 @@
      * notify cursor/anchor locations.
      *
      * @param cursorUpdateMode any combination of update modes and filters:
-     * {@link #CURSOR_UPDATE_IMMEDIATE}, {@link #CURSOR_UPDATE_MONITOR}, and date filters:
+     * {@link #CURSOR_UPDATE_IMMEDIATE}, {@link #CURSOR_UPDATE_MONITOR}, and data filters:
      * {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS}, {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS},
-     * {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER}.
+     * {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER},
+     * {@link #CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS},
+     * {@link #CURSOR_UPDATE_FILTER_TEXT_APPEARANCE}.
      * Pass {@code 0} to disable them. However, if an unknown flag is provided, request will be
      * rejected and method will return {@code false}.
      * @return {@code true} if the request is scheduled. {@code false} to indicate that when the
@@ -1240,7 +1242,9 @@
      * {@link #CURSOR_UPDATE_IMMEDIATE}, {@link #CURSOR_UPDATE_MONITOR}
      * @param cursorUpdateFilter any combination of data filters:
      * {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS}, {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS},
-     * {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER}.
+     * {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER},
+     * {@link #CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS},
+     * {@link #CURSOR_UPDATE_FILTER_TEXT_APPEARANCE}.
      *
      * <p>Pass {@code 0} to disable them. However, if an unknown flag is provided, request will be
      * rejected and method will return {@code false}.</p>
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 515b95c..82cf073 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1648,6 +1648,7 @@
      *
      * @param userId user ID to query
      * @return {@link List} of {@link InputMethodInfo}.
+     * @see #getEnabledInputMethodSubtypeListAsUser(String, boolean, int)
      * @hide
      */
     @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
@@ -1676,6 +1677,27 @@
     }
 
     /**
+     * Returns a list of enabled input method subtypes for the specified input method info for the
+     * specified user.
+     *
+     * @param imeId IME ID to be queried about.
+     * @param allowsImplicitlyEnabledSubtypes {@code true} to include implicitly enabled subtypes.
+     * @param userId user ID to be queried about.
+     *               {@link Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required if this is
+     *               different from the calling process user ID.
+     * @return {@link List} of {@link InputMethodSubtype}.
+     * @see #getEnabledInputMethodListAsUser(int)
+     * @hide
+     */
+    @NonNull
+    @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
+    public List<InputMethodSubtype> getEnabledInputMethodSubtypeListAsUser(
+            @NonNull String imeId, boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId) {
+        return IInputMethodManagerGlobalInvoker.getEnabledInputMethodSubtypeList(
+                Objects.requireNonNull(imeId), allowsImplicitlyEnabledSubtypes, userId);
+    }
+
+    /**
      * @deprecated Use {@link InputMethodService#showStatusIcon(int)} instead. This method was
      * intended for IME developers who should be accessing APIs through the service. APIs in this
      * class are intended for app developers interacting with the IME.
diff --git a/core/java/android/window/RemoteTransition.java b/core/java/android/window/RemoteTransition.java
index 4bd15f2..4cc7ec5 100644
--- a/core/java/android/window/RemoteTransition.java
+++ b/core/java/android/window/RemoteTransition.java
@@ -38,9 +38,18 @@
     /** The application thread that will be running the remote transition. */
     private @Nullable IApplicationThread mAppThread;
 
+    /** A name for this that can be used for debugging. */
+    private @Nullable String mDebugName;
+
     /** Constructs with no app thread (animation runs in shell). */
     public RemoteTransition(@NonNull IRemoteTransition remoteTransition) {
-        this(remoteTransition, null /* appThread */);
+        this(remoteTransition, null /* appThread */, null /* debugName */);
+    }
+
+    /** Constructs with no app thread (animation runs in shell). */
+    public RemoteTransition(@NonNull IRemoteTransition remoteTransition,
+            @Nullable String debugName) {
+        this(remoteTransition, null /* appThread */, debugName);
     }
 
     /** Get the IBinder associated with the underlying IRemoteTransition. */
@@ -70,15 +79,19 @@
      *   The actual remote-transition interface used to run the transition animation.
      * @param appThread
      *   The application thread that will be running the remote transition.
+     * @param debugName
+     *   A name for this that can be used for debugging.
      */
     @DataClass.Generated.Member
     public RemoteTransition(
             @NonNull IRemoteTransition remoteTransition,
-            @Nullable IApplicationThread appThread) {
+            @Nullable IApplicationThread appThread,
+            @Nullable String debugName) {
         this.mRemoteTransition = remoteTransition;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mRemoteTransition);
         this.mAppThread = appThread;
+        this.mDebugName = debugName;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -100,6 +113,14 @@
     }
 
     /**
+     * A name for this that can be used for debugging.
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getDebugName() {
+        return mDebugName;
+    }
+
+    /**
      * The actual remote-transition interface used to run the transition animation.
      */
     @DataClass.Generated.Member
@@ -119,6 +140,15 @@
         return this;
     }
 
+    /**
+     * A name for this that can be used for debugging.
+     */
+    @DataClass.Generated.Member
+    public @NonNull RemoteTransition setDebugName(@NonNull String value) {
+        mDebugName = value;
+        return this;
+    }
+
     @Override
     @DataClass.Generated.Member
     public String toString() {
@@ -127,7 +157,8 @@
 
         return "RemoteTransition { " +
                 "remoteTransition = " + mRemoteTransition + ", " +
-                "appThread = " + mAppThread +
+                "appThread = " + mAppThread + ", " +
+                "debugName = " + mDebugName +
         " }";
     }
 
@@ -139,9 +170,11 @@
 
         byte flg = 0;
         if (mAppThread != null) flg |= 0x2;
+        if (mDebugName != null) flg |= 0x4;
         dest.writeByte(flg);
         dest.writeStrongInterface(mRemoteTransition);
         if (mAppThread != null) dest.writeStrongInterface(mAppThread);
+        if (mDebugName != null) dest.writeString(mDebugName);
     }
 
     @Override
@@ -158,11 +191,13 @@
         byte flg = in.readByte();
         IRemoteTransition remoteTransition = IRemoteTransition.Stub.asInterface(in.readStrongBinder());
         IApplicationThread appThread = (flg & 0x2) == 0 ? null : IApplicationThread.Stub.asInterface(in.readStrongBinder());
+        String debugName = (flg & 0x4) == 0 ? null : in.readString();
 
         this.mRemoteTransition = remoteTransition;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mRemoteTransition);
         this.mAppThread = appThread;
+        this.mDebugName = debugName;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -182,10 +217,10 @@
     };
 
     @DataClass.Generated(
-            time = 1630690027011L,
+            time = 1678926409863L,
             codegenVersion = "1.0.23",
             sourceFile = "frameworks/base/core/java/android/window/RemoteTransition.java",
-            inputSignatures = "private @android.annotation.NonNull android.window.IRemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.app.IApplicationThread mAppThread\npublic @android.annotation.Nullable android.os.IBinder asBinder()\nclass RemoteTransition extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
+            inputSignatures = "private @android.annotation.NonNull android.window.IRemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.app.IApplicationThread mAppThread\nprivate @android.annotation.Nullable java.lang.String mDebugName\npublic @android.annotation.Nullable android.os.IBinder asBinder()\nclass RemoteTransition extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 4c482460..0f3eef7 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -149,8 +149,11 @@
     /** The task is launching behind home. */
     public static final int FLAG_TASK_LAUNCHING_BEHIND = 1 << 19;
 
+    /** The task became the top-most task even if it didn't change visibility. */
+    public static final int FLAG_MOVED_TO_TOP = 1 << 20;
+
     /** The first unused bit. This can be used by remotes to attach custom flags to this change. */
-    public static final int FLAG_FIRST_CUSTOM = 1 << 20;
+    public static final int FLAG_FIRST_CUSTOM = 1 << 21;
 
     /** The change belongs to a window that won't contain activities. */
     public static final int FLAGS_IS_NON_APP_WINDOW =
@@ -179,6 +182,7 @@
             FLAG_BACK_GESTURE_ANIMATED,
             FLAG_NO_ANIMATION,
             FLAG_TASK_LAUNCHING_BEHIND,
+            FLAG_MOVED_TO_TOP,
             FLAG_FIRST_CUSTOM
     })
     public @interface ChangeFlags {}
@@ -190,6 +194,9 @@
 
     private AnimationOptions mOptions;
 
+    /** This is only a BEST-EFFORT id used for log correlation. DO NOT USE for any real work! */
+    private int mDebugId = -1;
+
     /** @hide */
     public TransitionInfo(@TransitionType int type, @TransitionFlags int flags) {
         mType = type;
@@ -202,6 +209,7 @@
         in.readTypedList(mChanges, Change.CREATOR);
         in.readTypedList(mRoots, Root.CREATOR);
         mOptions = in.readTypedObject(AnimationOptions.CREATOR);
+        mDebugId = in.readInt();
     }
 
     @Override
@@ -212,6 +220,7 @@
         dest.writeTypedList(mChanges);
         dest.writeTypedList(mRoots, flags);
         dest.writeTypedObject(mOptions, flags);
+        dest.writeInt(mDebugId);
     }
 
     @NonNull
@@ -347,11 +356,24 @@
         return (mFlags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0;
     }
 
+    /**
+     * Set an arbitrary "debug" id for this info. This id will not be used for any "real work",
+     * it is just for debugging and logging.
+     */
+    public void setDebugId(int id) {
+        mDebugId = id;
+    }
+
+    /** Get the "debug" id of this info. Do NOT use this for real work, only use for debugging. */
+    public int getDebugId() {
+        return mDebugId;
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
-        sb.append("{t=").append(transitTypeToString(mType)).append(" f=0x")
-                .append(Integer.toHexString(mFlags)).append(" r=[");
+        sb.append("{id=").append(mDebugId).append(" t=").append(transitTypeToString(mType))
+                .append(" f=0x").append(Integer.toHexString(mFlags)).append(" r=[");
         for (int i = 0; i < mRoots.size(); ++i) {
             if (i > 0) {
                 sb.append(',');
@@ -510,6 +532,7 @@
      */
     public TransitionInfo localRemoteCopy() {
         final TransitionInfo out = new TransitionInfo(mType, mFlags);
+        out.mDebugId = mDebugId;
         for (int i = 0; i < mChanges.size(); ++i) {
             out.mChanges.add(mChanges.get(i).localRemoteCopy());
         }
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index ad20432..9f804b1 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -1586,61 +1586,109 @@
             return mShortcutInfo;
         }
 
+        /** Gets a string representation of a hierarchy-op type. */
+        public static String hopToString(int type) {
+            switch (type) {
+                case HIERARCHY_OP_TYPE_REPARENT: return "reparent";
+                case HIERARCHY_OP_TYPE_REORDER: return "reorder";
+                case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: return "ChildrenTasksReparent";
+                case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: return "SetLaunchRoot";
+                case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: return "SetAdjacentRoot";
+                case HIERARCHY_OP_TYPE_LAUNCH_TASK: return "LaunchTask";
+                case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: return "SetAdjacentFlagRoot";
+                case HIERARCHY_OP_TYPE_PENDING_INTENT: return "PendingIntent";
+                case HIERARCHY_OP_TYPE_START_SHORTCUT: return "StartShortcut";
+                case HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER: return "addInsetsFrameProvider";
+                case HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER:
+                    return "removeInsetsFrameProvider";
+                case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP: return "setAlwaysOnTop";
+                case HIERARCHY_OP_TYPE_REMOVE_TASK: return "RemoveTask";
+                case HIERARCHY_OP_TYPE_FINISH_ACTIVITY: return "finishActivity";
+                case HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS: return "ClearAdjacentRoot";
+                case HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH:
+                    return "setReparentLeafTaskIfRelaunch";
+                case HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION:
+                    return "addTaskFragmentOperation";
+                default: return "HOP(" + type + ")";
+            }
+        }
+
         @Override
         public String toString() {
+            StringBuilder sb = new StringBuilder();
+            sb.append("{").append(hopToString(mType)).append(": ");
             switch (mType) {
                 case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT:
-                    return "{ChildrenTasksReparent: from=" + mContainer + " to=" + mReparent
-                            + " mToTop=" + mToTop + " mReparentTopOnly=" + mReparentTopOnly
-                            + " mWindowingMode=" + Arrays.toString(mWindowingModes)
-                            + " mActivityType=" + Arrays.toString(mActivityTypes) + "}";
+                    sb.append("from=").append(mContainer).append(" to=").append(mReparent)
+                            .append(" mToTop=").append(mToTop)
+                            .append(" mReparentTopOnly=").append(mReparentTopOnly)
+                            .append(" mWindowingMode=").append(Arrays.toString(mWindowingModes))
+                            .append(" mActivityType=").append(Arrays.toString(mActivityTypes));
+                    break;
                 case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT:
-                    return "{SetLaunchRoot: container=" + mContainer
-                            + " mWindowingMode=" + Arrays.toString(mWindowingModes)
-                            + " mActivityType=" + Arrays.toString(mActivityTypes) + "}";
+                    sb.append("container=").append(mContainer)
+                            .append(" mWindowingMode=").append(Arrays.toString(mWindowingModes))
+                            .append(" mActivityType=").append(Arrays.toString(mActivityTypes));
+                    break;
                 case HIERARCHY_OP_TYPE_REPARENT:
-                    return "{reparent: " + mContainer + " to " + (mToTop ? "top of " : "bottom of ")
-                            + mReparent + "}";
+                    sb.append(mContainer).append(" to ").append(mToTop ? "top of " : "bottom of ")
+                            .append(mReparent);
+                    break;
                 case HIERARCHY_OP_TYPE_REORDER:
-                    return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}";
+                    sb.append(mContainer).append(" to ").append(mToTop ? "top" : "bottom");
+                    break;
                 case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
-                    return "{SetAdjacentRoot: container=" + mContainer
-                            + " adjacentRoot=" + mReparent + "}";
+                    sb.append("container=").append(mContainer)
+                            .append(" adjacentRoot=").append(mReparent);
+                    break;
                 case HIERARCHY_OP_TYPE_LAUNCH_TASK:
-                    return "{LaunchTask: " + mLaunchOptions + "}";
+                    sb.append(mLaunchOptions);
+                    break;
                 case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT:
-                    return "{SetAdjacentFlagRoot: container=" + mContainer + " clearRoot=" + mToTop
-                            + "}";
+                    sb.append("container=").append(mContainer).append(" clearRoot=").append(mToTop);
+                    break;
                 case HIERARCHY_OP_TYPE_START_SHORTCUT:
-                    return "{StartShortcut: options=" + mLaunchOptions + " info=" + mShortcutInfo
-                            + "}";
+                    sb.append("options=").append(mLaunchOptions)
+                            .append(" info=").append(mShortcutInfo);
+                    break;
+                case HIERARCHY_OP_TYPE_PENDING_INTENT:
+                    sb.append("options=").append(mLaunchOptions);
+                    break;
                 case HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER:
-                    return "{addRectInsetsProvider: container=" + mContainer
-                            + " provider=" + mInsetsFrameProvider + "}";
                 case HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER:
-                    return "{removeLocalInsetsProvider: container=" + mContainer
-                            + " provider=" + mInsetsFrameProvider + "}";
+                    sb.append("container=").append(mContainer)
+                            .append(" provider=").append(mInsetsFrameProvider);
+                    break;
                 case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP:
-                    return "{setAlwaysOnTop: container=" + mContainer
-                            + " alwaysOnTop=" + mAlwaysOnTop + "}";
+                    sb.append("container=").append(mContainer)
+                            .append(" alwaysOnTop=").append(mAlwaysOnTop);
+                    break;
                 case HIERARCHY_OP_TYPE_REMOVE_TASK:
-                    return "{RemoveTask: task=" + mContainer + "}";
+                    sb.append("task=").append(mContainer);
+                    break;
                 case HIERARCHY_OP_TYPE_FINISH_ACTIVITY:
-                    return "{finishActivity: activity=" + mContainer + "}";
+                    sb.append("activity=").append(mContainer);
+                    break;
                 case HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS:
-                    return "{ClearAdjacentRoot: container=" + mContainer + "}";
+                    sb.append("container=").append(mContainer);
+                    break;
                 case HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH:
-                    return "{setReparentLeafTaskIfRelaunch: container= " + mContainer
-                            + " reparentLeafTaskIfRelaunch= " + mReparentLeafTaskIfRelaunch + "}";
+                    sb.append("container= ").append(mContainer)
+                            .append(" reparentLeafTaskIfRelaunch= ")
+                            .append(mReparentLeafTaskIfRelaunch);
+                    break;
                 case HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION:
-                    return "{addTaskFragmentOperation: fragmentToken= " + mContainer
-                            + " operation= " + mTaskFragmentOperation + "}";
+                    sb.append("fragmentToken= ").append(mContainer)
+                            .append(" operation= ").append(mTaskFragmentOperation);
+                    break;
                 default:
-                    return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
-                            + " mToTop=" + mToTop
-                            + " mWindowingMode=" + Arrays.toString(mWindowingModes)
-                            + " mActivityType=" + Arrays.toString(mActivityTypes) + "}";
+                    sb.append("container=").append(mContainer)
+                            .append(" reparent=").append(mReparent)
+                            .append(" mToTop=").append(mToTop)
+                            .append(" mWindowingMode=").append(Arrays.toString(mWindowingModes))
+                            .append(" mActivityType=").append(Arrays.toString(mActivityTypes));
             }
+            return sb.append("}").toString();
         }
 
         @Override
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 0cb87fe..7ad2a68 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -524,11 +524,6 @@
     public static final String DEFAULT_QR_CODE_SCANNER = "default_qr_code_scanner";
 
     /**
-     * (boolean) Whether the task manager entrypoint is enabled.
-     */
-    public static final String TASK_MANAGER_ENABLED = "task_manager_enabled";
-
-    /**
      * (boolean) Whether the task manager should show an attention grabbing dot when tasks changed.
      */
     public static final String TASK_MANAGER_SHOW_FOOTER_DOT = "task_manager_show_footer_dot";
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index f7c03cd..ae58626 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -28,6 +28,7 @@
 import android.media.MediaRoute2Info;
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
+import android.view.KeyEvent;
 import android.service.notification.StatusBarNotification;
 
 import com.android.internal.statusbar.IAddTileResultCallback;
@@ -141,7 +142,7 @@
     void addQsTile(in ComponentName tile);
     void remQsTile(in ComponentName tile);
     void clickQsTile(in ComponentName tile);
-    void handleSystemKey(in int key);
+    void handleSystemKey(in KeyEvent key);
 
     /**
      * Methods to show toast messages for screen pinning
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index c1dbc87..3708859 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -29,6 +29,7 @@
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.UserHandle;
+import android.view.KeyEvent;
 import android.service.notification.StatusBarNotification;
 
 import com.android.internal.logging.InstanceId;
@@ -110,7 +111,7 @@
     void remTile(in ComponentName tile);
     void clickTile(in ComponentName tile);
     @UnsupportedAppUsage
-    void handleSystemKey(in int key);
+    void handleSystemKey(in KeyEvent key);
     int getLastSystemKey();
 
     /**
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 241320f..416d991 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -74,6 +74,7 @@
     WeakRefHandleField touchableRegionSurfaceControl;
     jfieldID transform;
     jfieldID windowToken;
+    jfieldID focusTransferTarget;
 } gInputWindowHandleClassInfo;
 
 static struct {
@@ -216,6 +217,17 @@
         mInfo.windowToken.clear();
     }
 
+    ScopedLocalRef<jobject>
+            focusTransferTargetObj(env,
+                                   env->GetObjectField(obj,
+                                                       gInputWindowHandleClassInfo
+                                                               .focusTransferTarget));
+    if (focusTransferTargetObj.get()) {
+        mInfo.focusTransferTarget = ibinderForJavaObject(env, focusTransferTargetObj.get());
+    } else {
+        mInfo.focusTransferTarget.clear();
+    }
+
     env->DeleteLocalRef(obj);
     return true;
 }
@@ -433,6 +445,9 @@
     GET_FIELD_ID(gInputWindowHandleClassInfo.windowToken, clazz, "windowToken",
                  "Landroid/os/IBinder;");
 
+    GET_FIELD_ID(gInputWindowHandleClassInfo.focusTransferTarget, clazz, "focusTransferTarget",
+                 "Landroid/os/IBinder;");
+
     jclass weakRefClazz;
     FIND_CLASS(weakRefClazz, "java/lang/ref/Reference");
 
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 03d6eec..e42c6f1 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1820,17 +1820,11 @@
 }
 
 static void nativeSetFocusedWindow(JNIEnv* env, jclass clazz, jlong transactionObj,
-                                   jobject toTokenObj, jstring windowNameJstr,
-                                   jobject focusedTokenObj, jstring focusedWindowNameJstr,
-                                   jint displayId) {
+                                   jobject toTokenObj, jstring windowNameJstr, jint displayId) {
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
     if (toTokenObj == NULL) return;
 
     sp<IBinder> toToken(ibinderForJavaObject(env, toTokenObj));
-    sp<IBinder> focusedToken;
-    if (focusedTokenObj != NULL) {
-        focusedToken = ibinderForJavaObject(env, focusedTokenObj);
-    }
 
     FocusRequest request;
     request.token = toToken;
@@ -1839,11 +1833,6 @@
         request.windowName = windowName.c_str();
     }
 
-    request.focusedToken = focusedToken;
-    if (focusedWindowNameJstr != NULL) {
-        ScopedUtfChars focusedWindowName(env, focusedWindowNameJstr);
-        request.focusedWindowName = focusedWindowName.c_str();
-    }
     request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
     request.displayId = displayId;
     transaction->setFocusedWindow(request);
@@ -2236,7 +2225,7 @@
             (void*)nativeGetHandle },
     {"nativeSetFixedTransformHint", "(JJI)V",
             (void*)nativeSetFixedTransformHint},
-    {"nativeSetFocusedWindow", "(JLandroid/os/IBinder;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;I)V",
+    {"nativeSetFocusedWindow", "(JLandroid/os/IBinder;Ljava/lang/String;I)V",
             (void*)nativeSetFocusedWindow},
     {"nativeRemoveCurrentInputFocus", "(JI)V",
             (void*)nativeRemoveCurrentInputFocus},
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index e6c8557..bb3089b 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -391,6 +391,9 @@
     optional int32 last_drop_input_mode = 36;
     optional int32 override_orientation = 37 [(.android.typedef) = "android.content.pm.ActivityInfo.ScreenOrientation"];
     optional bool should_send_compat_fake_focus = 38;
+    optional bool should_force_rotate_for_camera_compat = 39;
+    optional bool should_refresh_activity_for_camera_compat = 40;
+    optional bool should_refresh_activity_via_pause_for_camera_compat = 41;
 }
 
 /* represents WindowToken */
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 2e3d145..a64bb21 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -250,16 +250,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -282,7 +282,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <style name="Theme.DeviceDefault" parent="Theme.DeviceDefaultBase" />
@@ -345,16 +344,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -377,7 +376,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar.  This theme
@@ -439,16 +437,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -471,7 +469,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and
@@ -535,16 +532,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -567,7 +564,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent
@@ -630,16 +626,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -662,7 +658,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- DeviceDefault theme for dialog windows and activities. This changes the window to be
@@ -733,16 +728,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -765,7 +760,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog} that has a nice minimum width for a
@@ -827,16 +821,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -859,7 +853,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar -->
@@ -920,16 +913,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -952,7 +945,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width
@@ -1014,16 +1006,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1046,7 +1038,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -1124,16 +1115,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1156,7 +1147,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- DeviceDefault theme for a window without an action bar that will be displayed either
@@ -1219,16 +1209,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1251,7 +1241,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- DeviceDefault theme for a presentation window on a secondary display. -->
@@ -1312,16 +1301,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1344,7 +1333,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- DeviceDefault theme for panel windows. This removes all extraneous window
@@ -1407,16 +1395,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1439,7 +1427,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -1501,16 +1488,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1533,7 +1520,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -1595,16 +1581,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1627,7 +1613,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- DeviceDefault style for input methods, which is used by the
@@ -1689,16 +1674,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1721,7 +1706,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- DeviceDefault style for input methods, which is used by the
@@ -1783,16 +1767,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1815,7 +1799,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert">
@@ -1877,16 +1860,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1909,7 +1892,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- Theme for the dialog shown when an app crashes or ANRs. -->
@@ -1976,16 +1958,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2008,7 +1990,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame">
@@ -2068,16 +2049,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2100,7 +2081,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with a light-colored style -->
@@ -2298,16 +2278,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2330,8 +2310,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- Variant of the DeviceDefault (light) theme that has a solid (opaque) action bar with an
@@ -2393,16 +2371,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2425,8 +2403,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar -->
@@ -2487,16 +2463,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2519,8 +2495,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar.
@@ -2582,16 +2556,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2614,8 +2588,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar
@@ -2679,16 +2651,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2711,8 +2683,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} that has no title bar and translucent
@@ -2775,16 +2745,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2807,8 +2777,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be
@@ -2877,16 +2845,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2909,8 +2877,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} that has a nice minimum width for a
@@ -2975,16 +2941,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3007,11 +2973,9 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
-     <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar -->
+    <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar -->
     <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar" parent="Theme.Material.Light.Dialog.NoActionBar">
         <item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault.Light</item>
         <item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item>
@@ -3072,16 +3036,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3104,8 +3068,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog_NoActionBar} that has a nice minimum
@@ -3170,16 +3132,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3202,8 +3164,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -3249,16 +3209,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3281,8 +3241,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. -->
@@ -3328,16 +3286,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3360,8 +3318,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller
@@ -3426,16 +3382,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3458,8 +3414,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- DeviceDefault light theme for a window without an action bar that will be displayed either
@@ -3525,16 +3479,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3557,8 +3511,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- DeviceDefault light theme for a presentation window on a secondary display. -->
@@ -3622,16 +3574,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3654,8 +3606,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- DeviceDefault light theme for panel windows. This removes all extraneous window
@@ -3718,16 +3668,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3750,8 +3700,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.Alert">
@@ -3813,16 +3761,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3845,8 +3793,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <style name="Theme.DeviceDefault.Dialog.Alert.DayNight" parent="Theme.DeviceDefault.Light.Dialog.Alert" />
@@ -3908,16 +3854,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3940,8 +3886,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <style name="Theme.DeviceDefault.Light.Voice" parent="Theme.Material.Light.Voice">
@@ -4001,16 +3945,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4033,8 +3977,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- DeviceDefault theme for a window that should look like the Settings app.  -->
@@ -4347,16 +4289,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4379,7 +4321,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <style name="Theme.DeviceDefault.Settings.DialogBase" parent="Theme.Material.Light.BaseDialog">
@@ -4543,16 +4484,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4575,7 +4516,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.Alert">
@@ -4639,16 +4579,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4671,7 +4611,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" />
@@ -4813,16 +4752,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4845,8 +4784,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <!-- Theme overlay that replaces colorAccent with the colorAccent from {@link #Theme_DeviceDefault_DayNight}. -->
@@ -4870,16 +4807,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4902,7 +4839,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
 
     <style name="Theme.DeviceDefault.Light.Dialog.Alert.UserSwitchingDialog" parent="Theme.DeviceDefault.NoActionBar.Fullscreen">
@@ -4922,16 +4858,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
         <item name="materialColorErrorContainer">@color/system_error_container_light</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4954,8 +4890,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
         <item name="materialColorOnSurface">@color/system_on_surface_light</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-        <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
     </style>
 
     <style name="Theme.DeviceDefault.Notification" parent="@style/Theme.Material.Notification">
@@ -4986,16 +4920,16 @@
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
         <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
         <item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
-        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+        <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
         <item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
         <item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
         <item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
         <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
         <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
         <item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
-        <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+        <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
         <item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
-        <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+        <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
         <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
         <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
         <item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -5018,7 +4952,6 @@
         <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
         <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
         <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
     </style>
     <style name="Theme.DeviceDefault.AutofillHalfScreenDialogList" parent="Theme.DeviceDefault.DayNight">
         <item name="colorListDivider">@color/list_divider_opacity_device_default_light</item>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index bf8ca8b..4cccf8e 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -76,7 +76,8 @@
     <uses-permission android:name="android.permission.USE_CREDENTIALS" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.WRITE_CONTACTS" />
-    <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" />
+    <uses-permission android:name="android.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG" />
+    <uses-permission android:name="android.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
diff --git a/graphics/java/android/graphics/HardwareBufferRenderer.java b/graphics/java/android/graphics/HardwareBufferRenderer.java
index 361dc59..e04f13c 100644
--- a/graphics/java/android/graphics/HardwareBufferRenderer.java
+++ b/graphics/java/android/graphics/HardwareBufferRenderer.java
@@ -275,11 +275,22 @@
             Consumer<RenderResult> wrapped = consumable -> executor.execute(
                     () -> renderCallback.accept(consumable));
             if (!isClosed()) {
+                int renderWidth;
+                int renderHeight;
+                if (mTransform == SurfaceControl.BUFFER_TRANSFORM_ROTATE_90
+                        || mTransform == SurfaceControl.BUFFER_TRANSFORM_ROTATE_270) {
+                    renderWidth = mHardwareBuffer.getHeight();
+                    renderHeight = mHardwareBuffer.getWidth();
+                } else {
+                    renderWidth = mHardwareBuffer.getWidth();
+                    renderHeight = mHardwareBuffer.getHeight();
+                }
+
                 nRender(
                         mProxy,
                         mTransform,
-                        mHardwareBuffer.getWidth(),
-                        mHardwareBuffer.getHeight(),
+                        renderWidth,
+                        renderHeight,
                         mColorSpace.getNativeInstance(),
                         wrapped);
             } else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index e2cd7a0..d8e2f5c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -54,6 +54,7 @@
 import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
 import com.android.wm.shell.desktopmode.DesktopTasksController;
 import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler;
+import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler;
 import com.android.wm.shell.draganddrop.DragAndDropController;
 import com.android.wm.shell.freeform.FreeformComponents;
 import com.android.wm.shell.freeform.FreeformTaskListener;
@@ -677,13 +678,15 @@
             SyncTransactionQueue syncQueue,
             RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
             Transitions transitions,
-            EnterDesktopTaskTransitionHandler transitionHandler,
+            EnterDesktopTaskTransitionHandler enterDesktopTransitionHandler,
+            ExitDesktopTaskTransitionHandler exitDesktopTransitionHandler,
             @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
             @ShellMainThread ShellExecutor mainExecutor
     ) {
         return new DesktopTasksController(context, shellInit, shellController, displayController,
                 shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, transitions,
-                transitionHandler, desktopModeTaskRepository, mainExecutor);
+                enterDesktopTransitionHandler, exitDesktopTransitionHandler,
+                desktopModeTaskRepository, mainExecutor);
     }
 
     @WMSingleton
@@ -695,6 +698,15 @@
 
     @WMSingleton
     @Provides
+    static ExitDesktopTaskTransitionHandler provideExitDesktopTaskTransitionHandler(
+            Transitions transitions,
+            Context context
+    ) {
+        return new ExitDesktopTaskTransitionHandler(transitions, context);
+    }
+
+    @WMSingleton
+    @Provides
     @DynamicOverride
     static DesktopModeTaskRepository provideDesktopModeTaskRepository() {
         return new DesktopModeTaskRepository();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index cb04a43..c35cd5a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -68,7 +68,8 @@
         private val syncQueue: SyncTransactionQueue,
         private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
         private val transitions: Transitions,
-        private val animationTransitionHandler: EnterDesktopTaskTransitionHandler,
+        private val enterDesktopTaskTransitionHandler: EnterDesktopTaskTransitionHandler,
+        private val exitDesktopTaskTransitionHandler: ExitDesktopTaskTransitionHandler,
         private val desktopModeTaskRepository: DesktopModeTaskRepository,
         @ShellMainThread private val mainExecutor: ShellExecutor
 ) : RemoteCallable<DesktopTasksController>, Transitions.TransitionHandler {
@@ -149,7 +150,7 @@
         wct.setBounds(taskInfo.token, startBounds)
 
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-            animationTransitionHandler.startTransition(
+            enterDesktopTaskTransitionHandler.startTransition(
                     Transitions.TRANSIT_ENTER_FREEFORM, wct)
         } else {
             shellTaskOrganizer.applyTransaction(wct)
@@ -167,7 +168,8 @@
         wct.setBounds(taskInfo.token, freeformBounds)
 
         if (Transitions.ENABLE_SHELL_TRANSITIONS) {
-            animationTransitionHandler.startTransition(Transitions.TRANSIT_ENTER_DESKTOP_MODE, wct)
+            enterDesktopTaskTransitionHandler.startTransition(
+                Transitions.TRANSIT_ENTER_DESKTOP_MODE, wct)
         } else {
             shellTaskOrganizer.applyTransaction(wct)
         }
@@ -191,6 +193,18 @@
         }
     }
 
+    fun moveToFullscreenWithAnimation(task: ActivityManager.RunningTaskInfo) {
+        val wct = WindowContainerTransaction()
+        addMoveToFullscreenChanges(wct, task.token)
+
+        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+            exitDesktopTaskTransitionHandler.startTransition(
+            Transitions.TRANSIT_EXIT_DESKTOP_MODE, wct)
+        } else {
+            shellTaskOrganizer.applyTransaction(wct)
+        }
+    }
+
     /** Move a task to the front **/
     fun moveTaskToFront(taskInfo: ActivityManager.RunningTaskInfo) {
         val wct = WindowContainerTransaction()
@@ -396,9 +410,9 @@
         val statusBarHeight = displayController
                 .getDisplayLayout(taskInfo.displayId)?.stableInsets()?.top ?: 0
         if (y <= statusBarHeight && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
-            moveToFullscreen(taskInfo.taskId)
             visualIndicator?.releaseFullscreenIndicator()
             visualIndicator = null
+            moveToFullscreenWithAnimation(taskInfo)
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
new file mode 100644
index 0000000..d18e98a
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
+import android.animation.ValueAnimator;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.DisplayMetrics;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.transition.Transitions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Supplier;
+
+
+/**
+ * The {@link Transitions.TransitionHandler} that handles transitions for desktop mode tasks
+ * entering and exiting freeform.
+ */
+public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionHandler {
+    private static final int FULLSCREEN_ANIMATION_DURATION = 336;
+    private final Context mContext;
+    private final Transitions mTransitions;
+    private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
+
+    private Supplier<SurfaceControl.Transaction> mTransactionSupplier;
+
+    public ExitDesktopTaskTransitionHandler(
+            Transitions transitions,
+            Context context) {
+        this(transitions, SurfaceControl.Transaction::new, context);
+    }
+
+    private ExitDesktopTaskTransitionHandler(
+            Transitions transitions,
+            Supplier<SurfaceControl.Transaction> supplier,
+            Context context) {
+        mTransitions = transitions;
+        mTransactionSupplier = supplier;
+        mContext = context;
+    }
+
+    /**
+     * Starts Transition of a given type
+     * @param type Transition type
+     * @param wct WindowContainerTransaction for transition
+     */
+    public void startTransition(@WindowManager.TransitionType int type,
+            @NonNull WindowContainerTransaction wct) {
+        final IBinder token = mTransitions.startTransition(type, wct, this);
+        mPendingTransitionTokens.add(token);
+    }
+
+    @Override
+    public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+            @NonNull SurfaceControl.Transaction startT,
+            @NonNull SurfaceControl.Transaction finishT,
+            @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        boolean transitionHandled = false;
+        for (TransitionInfo.Change change : info.getChanges()) {
+            if ((change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0) {
+                continue;
+            }
+
+            final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+            if (taskInfo == null || taskInfo.taskId == -1) {
+                continue;
+            }
+
+            if (change.getMode() == WindowManager.TRANSIT_CHANGE) {
+                transitionHandled |= startChangeTransition(
+                        transition, info.getType(), change, startT, finishCallback);
+            }
+        }
+
+        mPendingTransitionTokens.remove(transition);
+
+        return transitionHandled;
+    }
+
+    @VisibleForTesting
+    boolean startChangeTransition(
+            @NonNull IBinder transition,
+            @WindowManager.TransitionType int type,
+            @NonNull TransitionInfo.Change change,
+            @NonNull SurfaceControl.Transaction startT,
+            @NonNull Transitions.TransitionFinishCallback finishCallback) {
+        if (!mPendingTransitionTokens.contains(transition)) {
+            return false;
+        }
+        final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+        if (type == Transitions.TRANSIT_EXIT_DESKTOP_MODE
+                && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+            // This Transition animates a task to fullscreen after being dragged to status bar
+            final Resources resources = mContext.getResources();
+            final DisplayMetrics metrics = resources.getDisplayMetrics();
+            final int screenWidth = metrics.widthPixels;
+            final int screenHeight = metrics.heightPixels;
+            final SurfaceControl sc = change.getLeash();
+            startT.setCrop(sc, null);
+            startT.apply();
+            final ValueAnimator animator = new ValueAnimator();
+            animator.setFloatValues(0f, 1f);
+            animator.setDuration(FULLSCREEN_ANIMATION_DURATION);
+            final Rect startBounds = change.getStartAbsBounds();
+            final float scaleX = (float) startBounds.width() / screenWidth;
+            final float scaleY = (float) startBounds.height() / screenHeight;
+            final SurfaceControl.Transaction t = mTransactionSupplier.get();
+            Point startPos = new Point(startBounds.left,
+                    startBounds.top);
+            animator.addUpdateListener(animation -> {
+                float fraction = animation.getAnimatedFraction();
+                float currentScaleX = scaleX + ((1 - scaleX) * fraction);
+                float currentScaleY = scaleY + ((1 - scaleY) * fraction);
+                t.setPosition(sc, startPos.x * (1 - fraction), startPos.y * (1 - fraction));
+                t.setScale(sc, currentScaleX, currentScaleY);
+                t.apply();
+            });
+            animator.start();
+            return true;
+        }
+
+        return false;
+    }
+
+    @Nullable
+    @Override
+    public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+            @NonNull TransitionRequestInfo request) {
+        return null;
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS
index afddfab..ec09827 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS
@@ -1,2 +1,3 @@
 # WM shell sub-module pip owner
 hwwang@google.com
+mateuszc@google.com
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 f2f30ea..984c3c1 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
@@ -73,6 +73,7 @@
 import android.window.TaskSnapshot;
 import android.window.WindowContainerToken;
 import android.window.WindowContainerTransaction;
+import android.window.WindowContainerTransactionCallback;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.protolog.common.ProtoLog;
@@ -142,6 +143,23 @@
     protected final ShellTaskOrganizer mTaskOrganizer;
     protected final ShellExecutor mMainExecutor;
 
+    // the runnable to execute after WindowContainerTransactions is applied to finish resizing pip
+    private Runnable mPipFinishResizeWCTRunnable;
+
+    private final WindowContainerTransactionCallback mPipFinishResizeWCTCallback =
+            new WindowContainerTransactionCallback() {
+        @Override
+        public void onTransactionReady(int id, SurfaceControl.Transaction t) {
+            t.apply();
+
+            // execute the runnable if non-null after WCT is applied to finish resizing pip
+            if (mPipFinishResizeWCTRunnable != null) {
+                mPipFinishResizeWCTRunnable.run();
+                mPipFinishResizeWCTRunnable = null;
+            }
+        }
+    };
+
     // These callbacks are called on the update thread
     private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
             new PipAnimationController.PipAnimationCallback() {
@@ -1249,8 +1267,23 @@
     /**
      * Animates resizing of the pinned stack given the duration and start bounds.
      * This is used when the starting bounds is not the current PiP bounds.
+     *
+     * @param pipFinishResizeWCTRunnable callback to run after window updates are complete
      */
     public void scheduleAnimateResizePip(Rect fromBounds, Rect toBounds, int duration,
+            float startingAngle, Consumer<Rect> updateBoundsCallback,
+            Runnable pipFinishResizeWCTRunnable) {
+        mPipFinishResizeWCTRunnable = pipFinishResizeWCTRunnable;
+        if (mPipFinishResizeWCTRunnable != null) {
+            ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+                    "mPipFinishResizeWCTRunnable is set to be called once window updates");
+        }
+
+        scheduleAnimateResizePip(fromBounds, toBounds, duration, startingAngle,
+                updateBoundsCallback);
+    }
+
+    private void scheduleAnimateResizePip(Rect fromBounds, Rect toBounds, int duration,
             float startingAngle, Consumer<Rect> updateBoundsCallback) {
         if (mWaitForFixedRotation) {
             ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -1555,7 +1588,7 @@
             mSplitScreenOptional.ifPresent(splitScreenController ->
                     splitScreenController.enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct));
         } else {
-            mTaskOrganizer.applyTransaction(wct);
+            mTaskOrganizer.applySyncTransaction(wct, mPipFinishResizeWCTCallback);
         }
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index fee9140..956af70 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -580,8 +580,16 @@
                 final float snapFraction = mPipBoundsAlgorithm.getSnapFraction(
                         mLastResizeBounds, movementBounds);
                 mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction);
+
+                // disable the pinch resizing until the final bounds are updated
+                final boolean prevEnablePinchResize = mEnablePinchResize;
+                mEnablePinchResize = false;
+
                 mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds,
-                        PINCH_RESIZE_SNAP_DURATION, mAngle, mUpdateResizeBoundsCallback);
+                        PINCH_RESIZE_SNAP_DURATION, mAngle, mUpdateResizeBoundsCallback, () -> {
+                            // reset the pinch resizing to its default state
+                            mEnablePinchResize = prevEnablePinchResize;
+                        });
             } else {
                 mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
                         PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE,
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 5c64177..c8d6a5e8 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
@@ -456,7 +456,10 @@
                         cancel(mWillFinishToHome);
                         return;
                     }
-                    hasChangingApp = true;
+                    // Don't consider order-only changes as changing apps.
+                    if (!TransitionUtil.isOrderOnly(change)) {
+                        hasChangingApp = true;
+                    }
                 }
             }
             if (hasChangingApp && foundRecentsClosing) {
@@ -484,13 +487,14 @@
             }
             boolean didMergeThings = false;
             if (closingTasks != null) {
-                // Cancelling a task-switch. Move the tasks back to mPausing from mOpening
+                // Potentially cancelling a task-switch. Move the tasks back to mPausing if they
+                // are in mOpening.
                 for (int i = 0; i < closingTasks.size(); ++i) {
                     final TransitionInfo.Change change = closingTasks.get(i);
                     int openingIdx = TaskState.indexOf(mOpeningTasks, change);
                     if (openingIdx < 0) {
-                        Slog.e(TAG, "Back to existing recents animation from an unrecognized "
-                                + "task: " + change.getTaskInfo().taskId);
+                        Slog.w(TAG, "Closing a task that wasn't opening, this may be split or"
+                                + " something unexpected: " + change.getTaskInfo().taskId);
                         continue;
                     }
                     mPausingTasks.add(mOpeningTasks.remove(openingIdx));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index d52abf7..5a92f78 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -235,6 +235,7 @@
     private TransitionInfo subCopy(@NonNull TransitionInfo info,
             @WindowManager.TransitionType int newType, boolean withChanges) {
         final TransitionInfo out = new TransitionInfo(newType, withChanges ? info.getFlags() : 0);
+        out.setDebugId(info.getDebugId());
         if (withChanges) {
             for (int i = 0; i < info.getChanges().size(); ++i) {
                 out.getChanges().add(info.getChanges().get(i));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 63c7969..3dd10a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -301,8 +301,8 @@
             return true;
         }
 
-        // check if no-animation and skip animation if so.
-        if (Transitions.isAllNoAnimation(info)) {
+        // Early check if the transition doesn't warrant an animation.
+        if (Transitions.isAllNoAnimation(info) || Transitions.isAllOrderOnly(info)) {
             startTransaction.apply();
             finishTransaction.apply();
             finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index 485b400..4e3d220 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -63,7 +63,7 @@
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         if (mTransition != transition) return false;
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Using registered One-shot remote"
-                + " transition %s for %s.", mRemote, transition);
+                + " transition %s for #%d.", mRemote, info.getDebugId());
 
         final IBinder.DeathRecipient remoteDied = () -> {
             Log.e(Transitions.TAG, "Remote transition died, finishing");
@@ -113,9 +113,6 @@
     public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Using registered One-shot remote"
-                + " transition %s for %s.", mRemote, transition);
-
         IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
             @Override
             public void onTransitionFinished(WindowContainerTransaction wct,
@@ -154,4 +151,10 @@
                 + " for %s: %s", transition, remote);
         return new WindowContainerTransaction();
     }
+
+    @Override
+    public String toString() {
+        return "OneShotRemoteHandler:" + mRemote.getDebugName() + ":"
+                + mRemote.getRemoteTransition();
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index 3c4e889..5b7231c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -101,8 +101,8 @@
         }
         RemoteTransition pendingRemote = mRequestedRemotes.get(transition);
         if (pendingRemote == null) {
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s doesn't have "
-                    + "explicit remote, search filters for match for %s", transition, info);
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition doesn't have "
+                    + "explicit remote, search filters for match for %s", info);
             // If no explicit remote, search filters until one matches
             for (int i = mFilters.size() - 1; i >= 0; --i) {
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Checking filter %s",
@@ -116,8 +116,8 @@
                 }
             }
         }
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Delegate animation for %s to %s",
-                transition, pendingRemote);
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Delegate animation for #%d to %s",
+                info.getDebugId(), pendingRemote);
 
         if (pendingRemote == null) return false;
 
@@ -184,9 +184,10 @@
     public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
-        final IRemoteTransition remote = mRequestedRemotes.get(mergeTarget).getRemoteTransition();
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Attempt merge %s into %s",
-                transition, remote);
+        final RemoteTransition remoteTransition = mRequestedRemotes.get(mergeTarget);
+        final IRemoteTransition remote = remoteTransition.getRemoteTransition();
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "   Merge into remote: %s",
+                remoteTransition);
         if (remote == null) return;
 
         IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java
index 0386ec3..1879bf7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.IBinder;
-import android.util.Log;
 import android.view.SurfaceControl;
 import android.window.TransitionInfo;
 import android.window.TransitionRequestInfo;
@@ -59,7 +58,6 @@
     @Override
     public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
             @Nullable SurfaceControl.Transaction finishTransaction) {
-        Log.e(Transitions.TAG, "Sleep transition was consumed. This doesn't make sense");
         mSleepTransitions.remove(transition);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 4284993..681fa51 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -140,6 +140,9 @@
     /** Transition type to freeform in desktop mode. */
     public static final int TRANSIT_ENTER_DESKTOP_MODE = WindowManager.TRANSIT_FIRST_CUSTOM + 11;
 
+    /** Transition type to fullscreen from desktop mode. */
+    public static final int TRANSIT_EXIT_DESKTOP_MODE = WindowManager.TRANSIT_FIRST_CUSTOM + 12;
+
     private final WindowOrganizer mOrganizer;
     private final Context mContext;
     private final ShellExecutor mMainExecutor;
@@ -182,6 +185,14 @@
 
         /** Ordered list of transitions which have been merged into this one. */
         private ArrayList<ActiveTransition> mMerged;
+
+        @Override
+        public String toString() {
+            if (mInfo != null && mInfo.getDebugId() >= 0) {
+                return "(#" + mInfo.getDebugId() + ")" + mToken;
+            }
+            return mToken.toString();
+        }
     }
 
     /** Keeps track of transitions which have been started, but aren't ready yet. */
@@ -516,6 +527,16 @@
         return hasNoAnimation;
     }
 
+    /**
+     * Check if all changes in this transition are only ordering changes. If so, we won't animate.
+     */
+    static boolean isAllOrderOnly(TransitionInfo info) {
+        for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+            if (!TransitionUtil.isOrderOnly(info.getChanges().get(i))) return false;
+        }
+        return true;
+    }
+
     @VisibleForTesting
     void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
             @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
@@ -529,8 +550,8 @@
                             activeTransition -> activeTransition.mToken).toArray()));
         }
         if (activeIdx > 0) {
-            Log.e(TAG, "Transition became ready out-of-order " + transitionToken + ". Expected"
-                    + " order: " + Arrays.toString(mPendingTransitions.stream().map(
+            Log.e(TAG, "Transition became ready out-of-order " + mPendingTransitions.get(activeIdx)
+                    + ". Expected order: " + Arrays.toString(mPendingTransitions.stream().map(
                             activeTransition -> activeTransition.mToken).toArray()));
         }
         // Move from pending to ready
@@ -547,6 +568,7 @@
         if (info.getType() == TRANSIT_SLEEP) {
             if (activeIdx > 0 || !mActiveTransitions.isEmpty() || mReadyTransitions.size() > 1) {
                 // Sleep starts a process of forcing all prior transitions to finish immediately
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Start finish-for-sleep");
                 finishForSleep(null /* forceFinish */);
                 return;
             }
@@ -555,8 +577,8 @@
         if (info.getRootCount() == 0 && !alwaysReportToKeyguard(info)) {
             // No root-leashes implies that the transition is empty/no-op, so just do
             // housekeeping and return.
-            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "No transition roots (%s): %s",
-                    transitionToken, info);
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "No transition roots in %s so"
+                    + " abort", active);
             onAbort(active);
             return;
         }
@@ -585,6 +607,8 @@
                 && allOccluded)) {
             // Treat this as an abort since we are bypassing any merge logic and effectively
             // finishing immediately.
+            ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+                    "Non-visible anim so abort: %s", active);
             onAbort(active);
             return;
         }
@@ -652,21 +676,21 @@
             return;
         }
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s ready while"
-                + " another transition %s is still animating. Notify the animating transition"
-                + " in case they can be merged", ready.mToken, playing.mToken);
+                + " %s is still animating. Notify the animating transition"
+                + " in case they can be merged", ready, playing);
         playing.mHandler.mergeAnimation(ready.mToken, ready.mInfo, ready.mStartT,
                 playing.mToken, (wct, cb) -> onMerged(playing, ready));
     }
 
     private void onMerged(@NonNull ActiveTransition playing, @NonNull ActiveTransition merged) {
-        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged %s",
-                merged.mToken);
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged: %s into %s",
+                merged, playing);
         int readyIdx = 0;
         if (mReadyTransitions.isEmpty() || mReadyTransitions.get(0) != merged) {
-            Log.e(TAG, "Merged transition out-of-order?");
+            Log.e(TAG, "Merged transition out-of-order? " + merged);
             readyIdx = mReadyTransitions.indexOf(merged);
             if (readyIdx < 0) {
-                Log.e(TAG, "Merged a transition that is no-longer queued?");
+                Log.e(TAG, "Merged a transition that is no-longer queued? " + merged);
                 return;
             }
         }
@@ -687,6 +711,7 @@
     }
 
     private void playTransition(@NonNull ActiveTransition active) {
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Playing animation for %s", active);
         for (int i = 0; i < mObservers.size(); ++i) {
             mObservers.get(i).onTransitionStarting(active.mToken);
         }
@@ -788,12 +813,12 @@
         int activeIdx = mActiveTransitions.indexOf(active);
         if (activeIdx < 0) {
             Log.e(TAG, "Trying to finish a non-running transition. Either remote crashed or "
-                    + " a handler didn't properly deal with a merge. " + active.mToken,
+                    + " a handler didn't properly deal with a merge. " + active,
                     new RuntimeException());
             return;
         } else if (activeIdx != 0) {
             // Relevant right now since we only allow 1 active transition at a time.
-            Log.e(TAG, "Finishing a transition out of order. " + active.mToken);
+            Log.e(TAG, "Finishing a transition out of order. " + active);
         }
         mActiveTransitions.remove(activeIdx);
 
@@ -801,7 +826,7 @@
             mObservers.get(i).onTransitionFinished(active.mToken, active.mAborted);
         }
         ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition animation finished "
-                + "(aborted=%b), notifying core %s", active.mAborted, active.mToken);
+                + "(aborted=%b), notifying core %s", active.mAborted, active);
         if (active.mStartT != null) {
             // Applied by now, so clear immediately to remove any references. Do not set to null
             // yet, though, since nullness is used later to disambiguate malformed transitions.
@@ -917,6 +942,8 @@
     /** Start a new transition directly. */
     public IBinder startTransition(@WindowManager.TransitionType int type,
             @NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) {
+        ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Directly starting a new transition "
+                + "type=%d wct=%s handler=%s", type, wct, handler);
         final ActiveTransition active = new ActiveTransition();
         active.mHandler = handler;
         active.mToken = mOrganizer.startNewTransition(type, wct);
@@ -948,8 +975,7 @@
             return;
         }
         if (forceFinish != null && mActiveTransitions.contains(forceFinish)) {
-            Log.e(TAG, "Forcing transition to finish due to sleep timeout: "
-                    + forceFinish.mToken);
+            Log.e(TAG, "Forcing transition to finish due to sleep timeout: " + forceFinish);
             forceFinish.mAborted = true;
             // Last notify of it being consumed. Note: mHandler should never be null,
             // but check just to be safe.
@@ -967,6 +993,8 @@
                 // Try to signal that we are sleeping by attempting to merge the sleep transition
                 // into the playing one.
                 final ActiveTransition nextSleep = mReadyTransitions.get(sleepIdx);
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Attempt to merge SLEEP %s"
+                        + " into %s", nextSleep, playing);
                 playing.mHandler.mergeAnimation(nextSleep.mToken, nextSleep.mInfo, dummyT,
                         playing.mToken, (wct, cb) -> {});
             } else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
index 7595c96..ce10291 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
@@ -31,6 +31,7 @@
 import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
 import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
 import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
 
 import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
@@ -90,6 +91,15 @@
                 && !change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
     }
 
+    /** Returns `true` if `change` is only re-ordering. */
+    public static boolean isOrderOnly(TransitionInfo.Change change) {
+        return change.getMode() == TRANSIT_CHANGE
+                && (change.getFlags() & FLAG_MOVED_TO_TOP) != 0
+                && change.getStartAbsBounds().equals(change.getEndAbsBounds())
+                && (change.getLastParent() == null
+                        || change.getLastParent().equals(change.getParent()));
+    }
+
     /**
      * Filter that selects leaf-tasks only. THIS IS ORDER-DEPENDENT! For it to work properly, you
      * MUST call `test` in the same order that the changes appear in the TransitionInfo.
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
index 91846fa..e986ee1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt
@@ -218,36 +218,36 @@
     assertLayers {
         if (landscapePosLeft) {
             splitAppLayerBoundsSnapToDivider(
-                component,
-                landscapePosLeft,
-                portraitPosTop,
-                scenario.endRotation
-            )
-            .then()
-            .isInvisible(component)
-            .then()
-            .splitAppLayerBoundsSnapToDivider(
-                component,
-                landscapePosLeft,
-                portraitPosTop,
-                scenario.endRotation
-            )
+                    component,
+                    landscapePosLeft,
+                    portraitPosTop,
+                    scenario.endRotation
+                )
+                .then()
+                .isInvisible(component)
+                .then()
+                .splitAppLayerBoundsSnapToDivider(
+                    component,
+                    landscapePosLeft,
+                    portraitPosTop,
+                    scenario.endRotation
+                )
         } else {
             splitAppLayerBoundsSnapToDivider(
-                component,
-                landscapePosLeft,
-                portraitPosTop,
-                scenario.endRotation
-            )
-            .then()
-            .isInvisible(component)
-            .then()
-            .splitAppLayerBoundsSnapToDivider(
-                component,
-                landscapePosLeft,
-                portraitPosTop,
-                scenario.endRotation
-            )
+                    component,
+                    landscapePosLeft,
+                    portraitPosTop,
+                    scenario.endRotation
+                )
+                .then()
+                .isInvisible(component)
+                .then()
+                .splitAppLayerBoundsSnapToDivider(
+                    component,
+                    landscapePosLeft,
+                    portraitPosTop,
+                    scenario.endRotation
+                )
         }
     }
 }
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
index 1045a5a..93ee699 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt
@@ -40,6 +40,7 @@
  *     Select "Auto-enter PiP" radio button
  *     Press Home button or swipe up to go Home and put [pipApp] in pip mode
  * ```
+ *
  * Notes:
  * ```
  *     1. All assertions are inherited from [EnterPipTest]
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
index 2d2588e..59918fb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt
@@ -38,6 +38,7 @@
  *     Launch an app in pip mode [pipApp],
  *     Swipe the pip window to the bottom-center of the screen and wait it disappear
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
index e540ad5..d165832 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt
@@ -38,6 +38,7 @@
  *     Click on the pip window
  *     Click on dismiss button and wait window disappear
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
index e079d547..db18edb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
@@ -53,6 +53,7 @@
  *     Launch [pipApp] on a fixed landscape orientation
  *     Broadcast action [ACTION_ENTER_PIP] to enter pip mode
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
index e40e5ea..51f0136 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt
@@ -44,9 +44,7 @@
     @Presubmit
     @Test
     open fun pipAppLayerAlwaysVisible() {
-        flicker.assertLayers {
-            this.isVisible(pipApp)
-        }
+        flicker.assertLayers { this.isVisible(pipApp) }
     }
 
     /** Checks the content overlay appears then disappears during the animation */
@@ -55,11 +53,7 @@
     open fun pipOverlayLayerAppearThenDisappear() {
         val overlay = ComponentNameMatcher.PIP_CONTENT_OVERLAY
         flicker.assertLayers {
-            this.notContains(overlay)
-                .then()
-                .contains(overlay)
-                .then()
-                .notContains(overlay)
+            this.notContains(overlay).then().contains(overlay).then().notContains(overlay)
         }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
index 1f060e9..f1925d8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt
@@ -35,6 +35,7 @@
  *     Launch an app in full screen
  *     Press an "enter pip" button to put [pipApp] in pip mode
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
index 313631c..43f7e00b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
@@ -42,6 +42,7 @@
  *     Expand [pipApp] app to full screen by clicking on the pip window and
  *     then on the expand button
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
index 93ffdd8d..617b3da 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
@@ -41,6 +41,7 @@
  *     Launch another full screen mode [testApp]
  *     Expand [pipApp] app to full screen via an intent
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
index 7d5f740..6deba1b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt
@@ -40,6 +40,7 @@
  *     Launch an app in pip mode [pipApp],
  *     Expand [pipApp] app to its maximum pip size by double clicking on it
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
index 9c00744..d8d57d2 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt
@@ -40,6 +40,7 @@
  *     Launch [testApp]
  *     Check if pip window moves down (visually)
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
index c8d5624..ae3f879 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt
@@ -40,6 +40,7 @@
  *     Press home
  *     Check if pip window moves up (visually)
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
index 083cfd2..4e2a4e7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
@@ -30,9 +30,7 @@
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
 
-/**
- * Test the dragging of a PIP window.
- */
+/** Test the dragging of a PIP window. */
 @RequiresDevice
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -59,9 +57,7 @@
                 pipApp.exit(wmHelper)
                 tapl.setEnableRotation(false)
             }
-            transitions {
-                pipApp.dragPipWindowAwayFromEdgeWithoutRelease(wmHelper, 50)
-            }
+            transitions { pipApp.dragPipWindowAwayFromEdgeWithoutRelease(wmHelper, 50) }
         }
 
     @Postsubmit
@@ -92,4 +88,4 @@
             return FlickerTestFactory.nonRotationTests()
         }
     }
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
index 53ce393..9fe9f52 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
@@ -16,10 +16,10 @@
 
 package com.android.wm.shell.flicker.pip
 
+import android.graphics.Rect
 import android.platform.test.annotations.Postsubmit
 import android.tools.common.Rotation
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.graphics.Rect
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.FlickerTest
 import android.tools.device.flicker.legacy.FlickerTestFactory
@@ -33,14 +33,12 @@
 import org.junit.runners.MethodSorters
 import org.junit.runners.Parameterized
 
-/**
- * Test the snapping of a PIP window via dragging, releasing, and checking its final location.
- */
+/** Test the snapping of a PIP window via dragging, releasing, and checking its final location. */
 @RequiresDevice
 @RunWith(Parameterized::class)
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class PipDragThenSnapTest(flicker: FlickerTest) : PipTransition(flicker){
+class PipDragThenSnapTest(flicker: FlickerTest) : PipTransition(flicker) {
     // represents the direction in which the pip window should be snapping
     private var willSnapRight: Boolean = true
 
@@ -60,8 +58,12 @@
 
                 // get the initial region bounds and cache them
                 val initRegion = pipApp.getWindowRect(wmHelper)
-                startBounds
-                        .set(initRegion.left, initRegion.top, initRegion.right, initRegion.bottom)
+                startBounds.set(
+                    initRegion.left,
+                    initRegion.top,
+                    initRegion.right,
+                    initRegion.bottom
+                )
 
                 // drag the pip window away from the edge
                 pipApp.dragPipWindowAwayFromEdge(wmHelper, 50)
@@ -108,4 +110,4 @@
             )
         }
     }
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
index 2cf8f61..703784d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt
@@ -43,6 +43,7 @@
  *     Rotate the screen from [flicker.scenario.startRotation] to [flicker.scenario.endRotation]
  *     (usually, 0->90 and 90->0)
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
index 17f174b..8938a2c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
@@ -107,9 +107,7 @@
         }
     }
 
-    @Presubmit
-    @Test
-    fun primaryAppWindowKeepVisible() = flicker.appWindowKeepVisible(primaryApp)
+    @Presubmit @Test fun primaryAppWindowKeepVisible() = flicker.appWindowKeepVisible(primaryApp)
 
     @Presubmit
     @Test
@@ -136,9 +134,7 @@
         )
 
     /** {@inheritDoc} */
-    @Presubmit
-    @Test
-    override fun entireScreenCovered() = super.entireScreenCovered()
+    @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
 
     companion object {
         @Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
index 5b06c9c..fc006ab 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
@@ -79,8 +79,13 @@
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
-    fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = false,
-            appExistAtStart = false)
+    fun cujCompleted() =
+        flicker.splitScreenEntered(
+            primaryApp,
+            secondaryApp,
+            fromOtherApp = false,
+            appExistAtStart = false
+        )
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
index c840183..95de744 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
@@ -82,8 +82,8 @@
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
-    fun cujCompleted() = flicker.splitScreenEntered(primaryApp, sendNotificationApp,
-            fromOtherApp = false)
+    fun cujCompleted() =
+        flicker.splitScreenEntered(primaryApp, sendNotificationApp, fromOtherApp = false)
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
index 5c99209..533d5dd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
@@ -80,8 +80,13 @@
     @IwTest(focusArea = "sysui")
     @Presubmit
     @Test
-    fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = false,
-            appExistAtStart = false)
+    fun cujCompleted() =
+        flicker.splitScreenEntered(
+            primaryApp,
+            secondaryApp,
+            fromOtherApp = false,
+            appExistAtStart = false
+        )
 
     @Presubmit
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 4ccc467..c9bd695 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -81,7 +81,8 @@
     @Mock lateinit var syncQueue: SyncTransactionQueue
     @Mock lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
     @Mock lateinit var transitions: Transitions
-    @Mock lateinit var transitionHandler: EnterDesktopTaskTransitionHandler
+    @Mock lateinit var exitDesktopTransitionHandler: ExitDesktopTaskTransitionHandler
+    @Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler
 
     lateinit var mockitoSession: StaticMockitoSession
     lateinit var controller: DesktopTasksController
@@ -117,7 +118,8 @@
             syncQueue,
             rootTaskDisplayAreaOrganizer,
             transitions,
-            transitionHandler,
+            enterDesktopTransitionHandler,
+            exitDesktopTransitionHandler,
             desktopModeTaskRepository,
             TestShellExecutor()
         )
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java
new file mode 100644
index 0000000..2c5a5cd
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.desktopmode;
+
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
+import static androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.app.WindowConfiguration;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.IBinder;
+import android.util.DisplayMetrics;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+import android.window.IWindowContainerToken;
+import android.window.TransitionInfo;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.transition.Transitions;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.function.Supplier;
+
+/** Tests of {@link com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler} */
+@SmallTest
+public class ExitDesktopTaskTransitionHandlerTest extends ShellTestCase {
+
+    @Mock
+    private Transitions mTransitions;
+    @Mock
+    IBinder mToken;
+    @Mock
+    Supplier<SurfaceControl.Transaction> mTransactionFactory;
+    @Mock
+    Context mContext;
+    @Mock
+    DisplayMetrics mDisplayMetrics;
+    @Mock
+    Resources mResources;
+    @Mock
+    SurfaceControl.Transaction mStartT;
+    @Mock
+    SurfaceControl.Transaction mFinishT;
+    @Mock
+    SurfaceControl.Transaction mAnimationT;
+    @Mock
+    Transitions.TransitionFinishCallback mTransitionFinishCallback;
+    @Mock
+    ShellExecutor mExecutor;
+
+    private ExitDesktopTaskTransitionHandler mExitDesktopTaskTransitionHandler;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        doReturn(mExecutor).when(mTransitions).getMainExecutor();
+        doReturn(mAnimationT).when(mTransactionFactory).get();
+        doReturn(mResources).when(mContext).getResources();
+        doReturn(mDisplayMetrics).when(mResources).getDisplayMetrics();
+        when(mResources.getDisplayMetrics())
+                .thenReturn(getContext().getResources().getDisplayMetrics());
+
+        mExitDesktopTaskTransitionHandler = new ExitDesktopTaskTransitionHandler(mTransitions,
+                mContext);
+    }
+
+    @Test
+    public void testTransitExitDesktopModeAnimation() throws Throwable {
+        final int transitionType = Transitions.TRANSIT_EXIT_DESKTOP_MODE;
+        final int taskId = 1;
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        doReturn(mToken).when(mTransitions)
+                .startTransition(transitionType, wct, mExitDesktopTaskTransitionHandler);
+
+        mExitDesktopTaskTransitionHandler.startTransition(transitionType, wct);
+
+        TransitionInfo.Change change =
+                createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FULLSCREEN);
+        TransitionInfo info = createTransitionInfo(Transitions.TRANSIT_EXIT_DESKTOP_MODE, change);
+        ArrayList<Exception> exceptions = new ArrayList<>();
+        runOnUiThread(() -> {
+            try {
+                assertTrue(mExitDesktopTaskTransitionHandler
+                        .startAnimation(mToken, info, mStartT, mFinishT,
+                                mTransitionFinishCallback));
+            } catch (Exception e) {
+                exceptions.add(e);
+            }
+        });
+        if (!exceptions.isEmpty()) {
+            throw exceptions.get(0);
+        }
+    }
+
+    private TransitionInfo.Change createChange(@WindowManager.TransitionType int type, int taskId,
+            @WindowConfiguration.WindowingMode int windowingMode) {
+        final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+        taskInfo.taskId = taskId;
+        taskInfo.token = new WindowContainerToken(mock(IWindowContainerToken.class));
+        taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
+        SurfaceControl.Builder b = new SurfaceControl.Builder()
+                .setName("test task");
+        final TransitionInfo.Change change = new TransitionInfo.Change(
+                taskInfo.token, b.build());
+        change.setMode(type);
+        change.setTaskInfo(taskInfo);
+        return change;
+    }
+
+    private static TransitionInfo createTransitionInfo(
+            @WindowManager.TransitionType int type, @NonNull TransitionInfo.Change change) {
+        TransitionInfo info = new TransitionInfo(type, 0);
+        info.addChange(change);
+        return info;
+    }
+
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index 5b62a94..ada3455 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -159,7 +159,7 @@
         mPipResizeGestureHandler.onPinchResize(upEvent);
 
         verify(mPipTaskOrganizer, times(1))
-                .scheduleAnimateResizePip(any(), any(), anyInt(), anyFloat(), any());
+                .scheduleAnimateResizePip(any(), any(), anyInt(), anyFloat(), any(), any());
 
         assertTrue("The new size should be bigger than the original PiP size.",
                 mPipResizeGestureHandler.getLastResizeBounds().width()
@@ -198,7 +198,7 @@
         mPipResizeGestureHandler.onPinchResize(upEvent);
 
         verify(mPipTaskOrganizer, times(1))
-                .scheduleAnimateResizePip(any(), any(), anyInt(), anyFloat(), any());
+                .scheduleAnimateResizePip(any(), any(), anyInt(), anyFloat(), any(), any());
 
         assertTrue("The new size should be smaller than the original PiP size.",
                 mPipResizeGestureHandler.getLastResizeBounds().width()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 1b29146..a9f311f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -181,7 +181,7 @@
 
         IBinder transition = mSplitScreenTransitions.startEnterTransition(
                 TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(),
-                new RemoteTransition(testRemote), mStageCoordinator, null, null);
+                new RemoteTransition(testRemote, "Test"), mStageCoordinator, null, null);
         mMainStage.onTaskAppeared(mMainChild, createMockSurface());
         mSideStage.onTaskAppeared(mSideChild, createMockSurface());
         boolean accepted = mStageCoordinator.startAnimation(transition, info,
@@ -407,7 +407,8 @@
         TransitionInfo enterInfo = createEnterPairInfo();
         IBinder enterTransit = mSplitScreenTransitions.startEnterTransition(
                 TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(),
-                new RemoteTransition(new TestRemoteTransition()), mStageCoordinator, null, null);
+                new RemoteTransition(new TestRemoteTransition(), "Test"),
+                mStageCoordinator, null, null);
         mMainStage.onTaskAppeared(mMainChild, createMockSurface());
         mSideStage.onTaskAppeared(mSideChild, createMockSurface());
         mStageCoordinator.startAnimation(enterTransit, enterInfo,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 60d6978..5cd548b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -277,7 +277,7 @@
         IBinder transitToken = new Binder();
         transitions.requestStartTransition(transitToken,
                 new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */,
-                        new RemoteTransition(testRemote)));
+                        new RemoteTransition(testRemote, "Test")));
         verify(mOrganizer, times(1)).startTransition(eq(transitToken), any());
         TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
                 .addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
@@ -422,7 +422,7 @@
                 new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()};
         filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
 
-        transitions.registerRemote(filter, new RemoteTransition(testRemote));
+        transitions.registerRemote(filter, new RemoteTransition(testRemote, "Test"));
         mMainExecutor.flushAll();
 
         IBinder transitToken = new Binder();
@@ -466,11 +466,12 @@
         final int transitType = TRANSIT_FIRST_CUSTOM + 1;
 
         OneShotRemoteHandler oneShot = new OneShotRemoteHandler(mMainExecutor,
-                new RemoteTransition(testRemote));
+                new RemoteTransition(testRemote, "Test"));
         // Verify that it responds to the remote but not other things.
         IBinder transitToken = new Binder();
         assertNotNull(oneShot.handleRequest(transitToken,
-                new TransitionRequestInfo(transitType, null, new RemoteTransition(testRemote))));
+                new TransitionRequestInfo(transitType, null,
+                        new RemoteTransition(testRemote, "Test"))));
         assertNull(oneShot.handleRequest(transitToken,
                 new TransitionRequestInfo(transitType, null, null)));
 
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 3b12972..5d79104 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -738,6 +738,9 @@
         "tests/unit/VectorDrawableTests.cpp",
         "tests/unit/WebViewFunctorManagerTests.cpp",
     ],
+    data: [
+        ":hwuimicro",
+    ],
 }
 
 // ------------------------
diff --git a/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp
index 768dfcd..706f18c 100644
--- a/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp
@@ -85,28 +85,20 @@
 }
 
 static SkMatrix createMatrixFromBufferTransform(SkScalar width, SkScalar height, int transform) {
-    auto matrix = SkMatrix();
     switch (transform) {
         case ANATIVEWINDOW_TRANSFORM_ROTATE_90:
-            matrix.setRotate(90);
-            matrix.postTranslate(width, 0);
-            break;
+            return SkMatrix::MakeAll(0, -1, height, 1, 0, 0, 0, 0, 1);
         case ANATIVEWINDOW_TRANSFORM_ROTATE_180:
-            matrix.setRotate(180);
-            matrix.postTranslate(width, height);
-            break;
+            return SkMatrix::MakeAll(-1, 0, width, 0, -1, height, 0, 0, 1);
         case ANATIVEWINDOW_TRANSFORM_ROTATE_270:
-            matrix.setRotate(270);
-            matrix.postTranslate(0, width);
-            break;
+            return SkMatrix::MakeAll(0, 1, 0, -1, 0, width, 0, 0, 1);
         default:
             ALOGE("Invalid transform provided. Transform should be validated from"
                   "the java side. Leveraging identity transform as a fallback");
             [[fallthrough]];
         case ANATIVEWINDOW_TRANSFORM_IDENTITY:
-            break;
+            return SkMatrix::I();
     }
-    return matrix;
 }
 
 static int android_graphics_HardwareBufferRenderer_render(JNIEnv* env, jobject, jlong renderProxy,
@@ -117,8 +109,8 @@
     auto skHeight = static_cast<SkScalar>(height);
     auto matrix = createMatrixFromBufferTransform(skWidth, skHeight, transform);
     auto colorSpace = GraphicsJNI::getNativeColorSpace(colorspacePtr);
-    proxy->setHardwareBufferRenderParams(
-            HardwareBufferRenderParams(matrix, colorSpace, createRenderCallback(env, consumer)));
+    proxy->setHardwareBufferRenderParams(HardwareBufferRenderParams(
+            width, height, matrix, colorSpace, createRenderCallback(env, consumer)));
     nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC);
     UiFrameInfoBuilder(proxy->frameInfo())
                 .setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID,
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 202a62c..cc987bc 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -69,15 +69,9 @@
 }
 
 Frame SkiaOpenGLPipeline::getFrame() {
-    if (mHardwareBuffer) {
-        AHardwareBuffer_Desc description;
-        AHardwareBuffer_describe(mHardwareBuffer, &description);
-        return Frame(description.width, description.height, 0);
-    } else {
-        LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
-                            "drawRenderNode called on a context with no surface!");
-        return mEglManager.beginFrame(mEglSurface);
-    }
+    LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
+                        "drawRenderNode called on a context with no surface!");
+    return mEglManager.beginFrame(mEglSurface);
 }
 
 IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw(
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 99298bc..c8f2e69 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -66,15 +66,8 @@
 }
 
 Frame SkiaVulkanPipeline::getFrame() {
-    if (mHardwareBuffer) {
-        AHardwareBuffer_Desc description;
-        AHardwareBuffer_describe(mHardwareBuffer, &description);
-        return Frame(description.width, description.height, 0);
-    } else {
-        LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr,
-                            "getFrame() called on a context with no surface!");
-        return vulkanManager().dequeueNextBuffer(mVkSurface);
-    }
+    LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr, "getFrame() called on a context with no surface!");
+    return vulkanManager().dequeueNextBuffer(mVkSurface);
 }
 
 IRenderPipeline::DrawResult SkiaVulkanPipeline::draw(
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index dd781bb..6b2c995 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -528,6 +528,14 @@
     sendLoadResetHint();
 }
 
+Frame CanvasContext::getFrame() {
+    if (mHardwareBuffer != nullptr) {
+        return {mBufferParams.getLogicalWidth(), mBufferParams.getLogicalHeight(), 0};
+    } else {
+        return mRenderPipeline->getFrame();
+    }
+}
+
 void CanvasContext::draw() {
     if (auto grContext = getGrContext()) {
         if (grContext->abandoned()) {
@@ -569,7 +577,8 @@
 
     mCurrentFrameInfo->markIssueDrawCommandsStart();
 
-    Frame frame = mRenderPipeline->getFrame();
+    Frame frame = getFrame();
+
     SkRect windowDirty = computeDirtyRect(frame, &dirty);
 
     ATRACE_FORMAT("Drawing " RECT_STRING, SK_RECT_ARGS(dirty));
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index b26c018..3f25339 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -264,6 +264,8 @@
 
     FrameInfo* getFrameInfoFromLast4(uint64_t frameNumber, uint32_t surfaceControlId);
 
+    Frame getFrame();
+
     // The same type as Frame.mWidth and Frame.mHeight
     int32_t mLastFrameWidth = 0;
     int32_t mLastFrameHeight = 0;
diff --git a/libs/hwui/renderthread/HardwareBufferRenderParams.h b/libs/hwui/renderthread/HardwareBufferRenderParams.h
index 91fe3f6..8c942d0 100644
--- a/libs/hwui/renderthread/HardwareBufferRenderParams.h
+++ b/libs/hwui/renderthread/HardwareBufferRenderParams.h
@@ -36,9 +36,12 @@
 class HardwareBufferRenderParams {
 public:
     HardwareBufferRenderParams() = default;
-    HardwareBufferRenderParams(const SkMatrix& transform, const sk_sp<SkColorSpace>& colorSpace,
+    HardwareBufferRenderParams(int32_t logicalWidth, int32_t logicalHeight,
+                               const SkMatrix& transform, const sk_sp<SkColorSpace>& colorSpace,
                                RenderCallback&& callback)
-            : mTransform(transform)
+            : mLogicalWidth(logicalWidth)
+            , mLogicalHeight(logicalHeight)
+            , mTransform(transform)
             , mColorSpace(colorSpace)
             , mRenderCallback(std::move(callback)) {}
     const SkMatrix& getTransform() const { return mTransform; }
@@ -50,7 +53,12 @@
         }
     }
 
+    int32_t getLogicalWidth() { return mLogicalWidth; }
+    int32_t getLogicalHeight() { return mLogicalHeight; }
+
 private:
+    int32_t mLogicalWidth;
+    int32_t mLogicalHeight;
     SkMatrix mTransform = SkMatrix::I();
     sk_sp<SkColorSpace> mColorSpace = SkColorSpace::MakeSRGB();
     RenderCallback mRenderCallback = nullptr;
diff --git a/media/aidl/android/media/soundtrigger_middleware/IInjectGlobalEvent.aidl b/media/aidl/android/media/soundtrigger_middleware/IInjectGlobalEvent.aidl
index dcf3945..47d8426 100644
--- a/media/aidl/android/media/soundtrigger_middleware/IInjectGlobalEvent.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/IInjectGlobalEvent.aidl
@@ -25,14 +25,14 @@
 oneway interface IInjectGlobalEvent {
 
     /**
-     * Request a fake STHAL restart.
+     * Trigger a fake STHAL restart.
      * This invalidates the {@link IInjectGlobalEvent}.
      */
     void triggerRestart();
 
     /**
-     * Triggers global resource contention into the fake STHAL. Loads/startRecognition
-     * will fail with RESOURCE_CONTENTION.
+     * Set global resource contention within the fake STHAL. Loads/startRecognition
+     * will fail with {@code RESOURCE_CONTENTION} until unset.
      * @param isContended - true to enable resource contention. false to disable resource contention
      *                      and resume normal functionality.
      * @param callback - Call {@link IAcknowledgeEvent#eventReceived()} on this interface once
@@ -40,4 +40,11 @@
      */
     void setResourceContention(boolean isContended, IAcknowledgeEvent callback);
 
+    /**
+     * Trigger an
+     * {@link android.hardware.soundtrigger3.ISoundTriggerHwGlobalCallback#onResourcesAvailable}
+     * callback from the fake STHAL. This callback is used to signal to the framework that
+     * previous operations which failed may now succeed.
+     */
+    void triggerOnResourcesAvailable();
 }
diff --git a/native/android/tests/activitymanager/nativeTests/Android.bp b/native/android/tests/activitymanager/nativeTests/Android.bp
index 528ac12..ebd7533 100644
--- a/native/android/tests/activitymanager/nativeTests/Android.bp
+++ b/native/android/tests/activitymanager/nativeTests/Android.bp
@@ -45,4 +45,7 @@
     required: [
         "UidImportanceHelperApp",
     ],
+    data: [
+        ":UidImportanceHelperApp",
+    ],
 }
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index c898fe5..d87abb9 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -20,7 +20,7 @@
     <string name="app_label">Companion Device Manager</string>
 
     <!-- Title of the device association confirmation dialog. -->
-    <string name="confirmation_title">Allow &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%1$s</xliff:g>&lt;/strong&gt; to access &lt;strong&gt;<xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g>&lt;/strong&gt;</string>
+    <string name="confirmation_title">Allow &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%1$s</xliff:g>&lt;/strong&gt; to access &lt;strong&gt;<xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g>&lt;/strong&gt;?</string>
 
     <!-- ================= DEVICE_PROFILE_WATCH and null profile ================= -->
 
@@ -34,7 +34,7 @@
     <string name="summary_watch">This app is needed to manage your <xliff:g id="device_name" example="My Watch">%1$s</xliff:g>. <xliff:g id="app_name" example="Android Wear">%2$s</xliff:g> will be allowed to sync info, like the name of someone calling, interact with your notifications and access your Phone, SMS, Contacts, Calendar, Call logs and Nearby devices permissions.</string>
 
     <!-- Description of the privileges the application will get if associated with the companion device of WATCH profile for singleDevice(type) [CHAR LIMIT=NONE] -->
-    <string name="summary_watch_single_device">This app will be allowed to sync info, like the name of someone calling, and access these permissions</string>
+    <string name="summary_watch_single_device">This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="device_name" example="phone">%1$s</xliff:g></string>
 
     <!-- ================= DEVICE_PROFILE_GLASSES ================= -->
 
@@ -48,7 +48,7 @@
     <string name="summary_glasses_multi_device">This app is needed to manage <xliff:g id="device_name" example="My Glasses">%1$s</xliff:g>. <xliff:g id="app_name" example="Glasses">%2$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts, Microphone and Nearby devices permissions.</string>
 
     <!-- Description of the privileges the application will get if associated with the companion device of GLASSES profile for singleDevice(type) [CHAR LIMIT=NONE] -->
-    <string name="summary_glasses_single_device">This app will be allowed to access these permissions on your phone</string>
+    <string name="summary_glasses_single_device">This app will be allowed to access these permissions on your <xliff:g id="device_name" example="phone">%1$s</xliff:g></string>
 
     <!-- ================= DEVICE_PROFILE_APP_STREAMING ================= -->
 
@@ -194,4 +194,10 @@
     <!-- Description of nearby_device_streaming permission of corresponding profile [CHAR LIMIT=NONE] -->
     <string name="permission_nearby_device_streaming_summary">Stream apps and other system features from your phone</string>
 
+    <!-- The type of the device for phone [CHAR LIMIT=30] -->
+    <string name="device_type" product="default">phone</string>
+
+    <!-- The type of the device for tablet [CHAR LIMIT=30] -->
+    <string name="device_type" product="tablet">tablet</string>
+
 </resources>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index ae08823..4154029 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -551,7 +551,8 @@
             summary = getHtmlFromResources(this, SUMMARIES.get(null), deviceName);
             mConstraintList.setVisibility(View.GONE);
         } else {
-            summary = getHtmlFromResources(this, SUMMARIES.get(deviceProfile));
+            summary = getHtmlFromResources(
+                    this, SUMMARIES.get(deviceProfile), getString(R.string.device_type));
             mPermissionTypes.addAll(PERMISSION_TYPES.get(deviceProfile));
             setupPermissionList();
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index a3d632c..e884cf8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -305,10 +305,11 @@
         synchronized (mProfileLock) {
             if (getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
                 for (CachedBluetoothDevice member : getMemberDevice()) {
-                    Log.d(TAG, "Disconnect the member(" + member.getAddress() + ")");
+                    Log.d(TAG, "Disconnect the member:" + member);
                     member.disconnect();
                 }
             }
+            Log.d(TAG, "Disconnect " + this);
             mDevice.disconnect();
         }
         // Disconnect  PBAP server in case its connected
@@ -440,11 +441,11 @@
                 Log.d(TAG, "No profiles. Maybe we will connect later for device " + mDevice);
                 return;
             }
-
+            Log.d(TAG, "connect " + this);
             mDevice.connect();
             if (getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
                 for (CachedBluetoothDevice member : getMemberDevice()) {
-                    Log.d(TAG, "connect the member(" + member.getAddress() + ")");
+                    Log.d(TAG, "connect the member:" + member);
                     member.connect();
                 }
             }
@@ -530,7 +531,7 @@
     }
 
     // TODO: do any of these need to run async on a background thread?
-    private void fillData() {
+    void fillData() {
         updateProfiles();
         fetchActiveDevices();
         migratePhonebookPermissionChoice();
@@ -933,14 +934,15 @@
 
     @Override
     public String toString() {
-        return "CachedBluetoothDevice ("
+        return "CachedBluetoothDevice{"
                 + "anonymizedAddress="
                 + mDevice.getAnonymizedAddress()
                 + ", name="
                 + getName()
                 + ", groupId="
                 + mGroupId
-                + ")";
+                + ", member=" + mMemberDevices
+                + "}";
     }
 
     @Override
@@ -1482,6 +1484,7 @@
      * Store the member devices that are in the same coordinated set.
      */
     public void addMemberDevice(CachedBluetoothDevice memberDevice) {
+        Log.d(TAG, this + " addMemberDevice = " + memberDevice);
         mMemberDevices.add(memberDevice);
     }
 
@@ -1511,13 +1514,14 @@
         mDevice = newMainDevice.mDevice;
         mRssi = newMainDevice.mRssi;
         mJustDiscovered = newMainDevice.mJustDiscovered;
+        fillData();
 
         // Set sub device from backup
         newMainDevice.release();
         newMainDevice.mDevice = tmpDevice;
         newMainDevice.mRssi = tmpRssi;
         newMainDevice.mJustDiscovered = tmpJustDiscovered;
-        fetchActiveDevices();
+        newMainDevice.fillData();
     }
 
     /**
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index 20a6cd8..356bb82 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -71,7 +71,7 @@
                 return BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
             }
 
-            for (Map.Entry<Integer, ParcelUuid> entry: groupIdMap.entrySet()) {
+            for (Map.Entry<Integer, ParcelUuid> entry : groupIdMap.entrySet()) {
                 if (entry.getValue().equals(BluetoothUuid.CAP)) {
                     return entry.getKey();
                 }
@@ -153,72 +153,13 @@
             return;
         }
         log("onGroupIdChanged: mCachedDevices list =" + mCachedDevices.toString());
-        final LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
-        final CachedBluetoothDeviceManager deviceManager = mBtManager.getCachedDeviceManager();
-        final LeAudioProfile leAudioProfile = profileManager.getLeAudioProfile();
-        final BluetoothDevice mainBluetoothDevice = (leAudioProfile != null && isAtLeastT()) ?
-                leAudioProfile.getConnectedGroupLeadDevice(groupId) : null;
+        List<CachedBluetoothDevice> memberDevicesList = getMemberDevicesList(groupId);
         CachedBluetoothDevice newMainDevice =
-                mainBluetoothDevice != null ? deviceManager.findDevice(mainBluetoothDevice) : null;
-        if (newMainDevice != null) {
-            final CachedBluetoothDevice finalNewMainDevice = newMainDevice;
-            final List<CachedBluetoothDevice> memberDevices = mCachedDevices.stream()
-                    .filter(cachedDevice -> !cachedDevice.equals(finalNewMainDevice)
-                            && cachedDevice.getGroupId() == groupId)
-                    .collect(Collectors.toList());
-            if (memberDevices == null || memberDevices.isEmpty()) {
-                log("onGroupIdChanged: There is no member device in list.");
-                return;
-            }
-            log("onGroupIdChanged: removed from UI device =" + memberDevices
-                    + ", with groupId=" + groupId + " mainDevice= " + newMainDevice);
-            for (CachedBluetoothDevice memberDeviceItem : memberDevices) {
-                Set<CachedBluetoothDevice> memberSet = memberDeviceItem.getMemberDevice();
-                if (!memberSet.isEmpty()) {
-                    log("onGroupIdChanged: Transfer the member list into new main device.");
-                    for (CachedBluetoothDevice memberListItem : memberSet) {
-                        if (!memberListItem.equals(newMainDevice)) {
-                            newMainDevice.addMemberDevice(memberListItem);
-                        }
-                    }
-                    memberSet.clear();
-                }
+                getPreferredMainDeviceWithoutConectionState(groupId, memberDevicesList);
 
-                newMainDevice.addMemberDevice(memberDeviceItem);
-                mCachedDevices.remove(memberDeviceItem);
-                mBtManager.getEventManager().dispatchDeviceRemoved(memberDeviceItem);
-            }
-
-            if (!mCachedDevices.contains(newMainDevice)) {
-                mCachedDevices.add(newMainDevice);
-                mBtManager.getEventManager().dispatchDeviceAdded(newMainDevice);
-            }
-        } else {
-            log("onGroupIdChanged: There is no main device from the LE profile.");
-            int firstMatchedIndex = -1;
-
-            for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
-                final CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
-                if (cachedDevice.getGroupId() != groupId) {
-                    continue;
-                }
-
-                if (firstMatchedIndex == -1) {
-                    // Found the first one
-                    firstMatchedIndex = i;
-                    newMainDevice = cachedDevice;
-                    continue;
-                }
-
-                log("onGroupIdChanged: removed from UI device =" + cachedDevice
-                        + ", with groupId=" + groupId + " firstMatchedIndex=" + firstMatchedIndex);
-
-                newMainDevice.addMemberDevice(cachedDevice);
-                mCachedDevices.remove(i);
-                mBtManager.getEventManager().dispatchDeviceRemoved(cachedDevice);
-                break;
-            }
-        }
+        log("onGroupIdChanged: The mainDevice= " + newMainDevice
+                + " and the memberDevicesList of groupId= " + groupId + " =" + memberDevicesList);
+        addMemberDevicesIntoMainDevice(memberDevicesList, newMainDevice);
     }
 
     // @return {@code true}, the event is processed inside the method. It is for updating
@@ -263,7 +204,7 @@
                     break;
                 }
 
-                for (CachedBluetoothDevice device: memberSet) {
+                for (CachedBluetoothDevice device : memberSet) {
                     if (device.isConnected()) {
                         log("set device: " + device + " as the main device");
                         // Main device is disconnected and sub device is connected
@@ -296,7 +237,7 @@
                     continue;
                 }
 
-                for (CachedBluetoothDevice memberDevice: memberSet) {
+                for (CachedBluetoothDevice memberDevice : memberSet) {
                     if (memberDevice != null && memberDevice.equals(device)) {
                         return cachedDevice;
                     }
@@ -310,7 +251,6 @@
      * Check if the {@code groupId} is existed.
      *
      * @param groupId The group id
-     *
      * @return {@code true}, if we could find a device with this {@code groupId}; Otherwise,
      * return {@code false}.
      */
@@ -322,6 +262,116 @@
         return false;
     }
 
+    private List<CachedBluetoothDevice> getMemberDevicesList(int groupId) {
+        return mCachedDevices.stream()
+                .filter(cacheDevice -> cacheDevice.getGroupId() == groupId)
+                .collect(Collectors.toList());
+    }
+
+    private CachedBluetoothDevice getPreferredMainDeviceWithoutConectionState(int groupId,
+            List<CachedBluetoothDevice> memberDevicesList) {
+        // First, priority connected lead device from LE profile
+        // Second, the DUAL mode device which has A2DP/HFP and LE audio
+        // Last, any one of LE device in the list.
+        if (memberDevicesList == null || memberDevicesList.isEmpty()) {
+            return null;
+        }
+
+        final LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
+        final CachedBluetoothDeviceManager deviceManager = mBtManager.getCachedDeviceManager();
+        final LeAudioProfile leAudioProfile = profileManager.getLeAudioProfile();
+        final BluetoothDevice mainBluetoothDevice = (leAudioProfile != null && isAtLeastT())
+                ? leAudioProfile.getConnectedGroupLeadDevice(groupId) : null;
+
+        if (mainBluetoothDevice != null) {
+            log("getPreferredMainDevice: The LeadDevice from LE profile is "
+                    + mainBluetoothDevice.getAnonymizedAddress());
+        }
+
+        // 1st
+        CachedBluetoothDevice newMainDevice =
+                mainBluetoothDevice != null ? deviceManager.findDevice(mainBluetoothDevice) : null;
+        if (newMainDevice != null) {
+            if (newMainDevice.isConnected()) {
+                log("getPreferredMainDevice: The connected LeadDevice from LE profile");
+                return newMainDevice;
+            } else {
+                log("getPreferredMainDevice: The LeadDevice is not connect.");
+            }
+        } else {
+            log("getPreferredMainDevice: The LeadDevice is not in the all of devices list");
+        }
+
+        // 2nd
+        newMainDevice = memberDevicesList.stream()
+                .filter(cachedDevice -> cachedDevice.getConnectableProfiles().stream()
+                        .anyMatch(profile -> profile instanceof A2dpProfile
+                                || profile instanceof HeadsetProfile))
+                .findFirst().orElse(null);
+        if (newMainDevice != null) {
+            log("getPreferredMainDevice: The DUAL mode device");
+            return newMainDevice;
+        }
+
+        // last
+        if (!memberDevicesList.isEmpty()) {
+            newMainDevice = memberDevicesList.get(0);
+        }
+        return newMainDevice;
+    }
+
+    private void addMemberDevicesIntoMainDevice(List<CachedBluetoothDevice> memberDevicesList,
+            CachedBluetoothDevice newMainDevice) {
+        if (newMainDevice == null) {
+            log("addMemberDevicesIntoMainDevice: No main device. Do nothing.");
+            return;
+        }
+        if (memberDevicesList.isEmpty()) {
+            log("addMemberDevicesIntoMainDevice: No member device in list. Do nothing.");
+            return;
+        }
+        CachedBluetoothDevice mainDeviceOfNewMainDevice = findMainDevice(newMainDevice);
+        boolean isMemberInOtherMainDevice = mainDeviceOfNewMainDevice != null;
+        if (!memberDevicesList.contains(newMainDevice) && isMemberInOtherMainDevice) {
+            log("addMemberDevicesIntoMainDevice: The 'new main device' is not in list, and it is "
+                    + "the member at other device. Do switch main and member.");
+            // To switch content and dispatch to notify UI change
+            mBtManager.getEventManager().dispatchDeviceRemoved(mainDeviceOfNewMainDevice);
+            mainDeviceOfNewMainDevice.switchMemberDeviceContent(newMainDevice);
+            mainDeviceOfNewMainDevice.refresh();
+            // It is necessary to do remove and add for updating the mapping on
+            // preference and device
+            mBtManager.getEventManager().dispatchDeviceAdded(mainDeviceOfNewMainDevice);
+        } else {
+            log("addMemberDevicesIntoMainDevice: Set new main device");
+            for (CachedBluetoothDevice memberDeviceItem : memberDevicesList) {
+                if (memberDeviceItem.equals(newMainDevice)) {
+                    continue;
+                }
+                Set<CachedBluetoothDevice> memberSet = memberDeviceItem.getMemberDevice();
+                if (!memberSet.isEmpty()) {
+                    for (CachedBluetoothDevice memberSetItem : memberSet) {
+                        if (!memberSetItem.equals(newMainDevice)) {
+                            newMainDevice.addMemberDevice(memberSetItem);
+                        }
+                    }
+                    memberSet.clear();
+                }
+
+                newMainDevice.addMemberDevice(memberDeviceItem);
+                mCachedDevices.remove(memberDeviceItem);
+                mBtManager.getEventManager().dispatchDeviceRemoved(memberDeviceItem);
+            }
+
+            if (!mCachedDevices.contains(newMainDevice)) {
+                mCachedDevices.add(newMainDevice);
+                mBtManager.getEventManager().dispatchDeviceAdded(newMainDevice);
+            }
+        }
+        log("addMemberDevicesIntoMainDevice: After changed, CachedBluetoothDevice list: "
+                + mCachedDevices);
+    }
+
     private void log(String msg) {
         if (DEBUG) {
             Log.d(TAG, msg);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 1c179f8..6444f3b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -1150,9 +1150,11 @@
         assertThat(mCachedDevice.mRssi).isEqualTo(RSSI_2);
         assertThat(mCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_2);
         assertThat(mCachedDevice.mDevice).isEqualTo(mSubDevice);
+        verify(mCachedDevice).fillData();
         assertThat(mSubCachedDevice.mRssi).isEqualTo(RSSI_1);
         assertThat(mSubCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_1);
         assertThat(mSubCachedDevice.mDevice).isEqualTo(mDevice);
+        verify(mSubCachedDevice).fillData();
         assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
     }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/OWNERS b/packages/SettingsProvider/src/com/android/providers/settings/OWNERS
new file mode 100644
index 0000000..0b71816
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/OWNERS
@@ -0,0 +1 @@
+per-file WritableNamespacePrefixes.java = cbrubaker@google.com,tedbauer@google.com
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 5a8c594..3e1b597 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2323,7 +2323,15 @@
             return;
         } else if (hasAllowlistPermission) {
             for (String flag : flags) {
-                if (!DeviceConfig.getAdbWritableFlags().contains(flag)) {
+                boolean namespaceAllowed = false;
+                for (String allowlistedPrefix : WritableNamespacePrefixes.ALLOWLIST) {
+                    if (flag.startsWith(allowlistedPrefix)) {
+                        namespaceAllowed = true;
+                        break;
+                    }
+                }
+
+                if (!namespaceAllowed && !DeviceConfig.getAdbWritableFlags().contains(flag)) {
                     throw new SecurityException("Permission denial for flag '"
                         + flag
                         + "'; allowlist permission granted, but must add flag to the allowlist.");
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespacePrefixes.java b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespacePrefixes.java
new file mode 100644
index 0000000..28f25e0
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespacePrefixes.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.providers.settings;
+
+import android.util.ArraySet;
+
+import java.util.Arrays;
+import java.util.Set;
+
+/**
+ * Contains the list of prefixes for namespaces in which any flag can be written with adb.
+ * <p>
+ * A security review is required for any prefix that's added to this list. To add to
+ * the list, create a change and tag the OWNER. In the change description, include a
+ * description of the flag's functionality, and a justification for why it needs to be
+ * allowlisted.
+ */
+final class WritableNamespacePrefixes {
+    public static final Set<String> ALLOWLIST =
+            new ArraySet<String>(Arrays.asList(
+                "app_compat_overrides",
+                "game_overlay",
+                "namespace1"
+            ));
+}
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index ac75cc8..3007d4a 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -182,7 +182,6 @@
         "androidx.dynamicanimation_dynamicanimation",
         "androidx-constraintlayout_constraintlayout",
         "androidx.exifinterface_exifinterface",
-        "androidx.test.ext.junit",
         "com.google.android.material_material",
         "kotlinx_coroutines_android",
         "kotlinx_coroutines",
@@ -191,6 +190,7 @@
         "SystemUI-proto",
         "monet",
         "dagger2",
+        "jsr305",
         "jsr330",
         "lottie",
         "LowLightDreamLib",
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
index 91cb4ba..462c90b 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
@@ -56,6 +56,7 @@
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:paddingStart="16dp"
+      android:paddingEnd="16dp"
       android:gravity="center_vertical"
       android:textColor="@color/colorControlNormal"
       android:textSize="@dimen/label_text_size"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
index 4b6f9a4..02d279f 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
@@ -24,7 +24,9 @@
 import android.os.Bundle;
 import android.provider.Browser;
 import android.provider.Settings;
+import android.view.View;
 
+import androidx.annotation.Nullable;
 import androidx.fragment.app.FragmentActivity;
 import androidx.preference.Preference;
 import androidx.preference.PreferenceFragmentCompat;
@@ -56,6 +58,13 @@
             initializeHelpAndFeedbackPreference();
         }
 
+        @Override
+        public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+            super.onViewCreated(view, savedInstanceState);
+            view.setLayoutDirection(
+                    view.getResources().getConfiguration().getLayoutDirection());
+        }
+
         /**
          * Returns large buttons settings state.
          *
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
index a25790a..5b7bbe8 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
@@ -194,13 +194,15 @@
     /** Updates a11y menu layout position by configuring layout params. */
     private void updateLayoutPosition() {
         final Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
-        final int orientation = mService.getResources().getConfiguration().orientation;
+        final Configuration configuration = mService.getResources().getConfiguration();
+        final int orientation = configuration.orientation;
         if (display != null && orientation == Configuration.ORIENTATION_LANDSCAPE) {
+            final boolean ltr = configuration.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
             switch (display.getRotation()) {
-                case Surface.ROTATION_90:
+                case Surface.ROTATION_0:
                 case Surface.ROTATION_180:
                     mLayoutParameter.gravity =
-                            Gravity.END | Gravity.BOTTOM
+                            (ltr ? Gravity.END : Gravity.START) | Gravity.BOTTOM
                                     | Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL;
                     mLayoutParameter.width = WindowManager.LayoutParams.WRAP_CONTENT;
                     mLayoutParameter.height = WindowManager.LayoutParams.MATCH_PARENT;
@@ -208,10 +210,10 @@
                     mLayoutParameter.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
                     mLayout.setBackgroundResource(R.drawable.shadow_90deg);
                     break;
-                case Surface.ROTATION_0:
+                case Surface.ROTATION_90:
                 case Surface.ROTATION_270:
                     mLayoutParameter.gravity =
-                            Gravity.START | Gravity.BOTTOM
+                            (ltr ? Gravity.START : Gravity.END) | Gravity.BOTTOM
                                     | Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL;
                     mLayoutParameter.width = WindowManager.LayoutParams.WRAP_CONTENT;
                     mLayoutParameter.height = WindowManager.LayoutParams.MATCH_PARENT;
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
index 03e1e66..197b217 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
@@ -384,8 +384,15 @@
         }
 
         @JvmStatic
-        fun adaptRemoteAnimation(adapter: RemoteAnimationAdapter): RemoteTransition {
-            return RemoteTransition(adaptRemoteRunner(adapter.runner), adapter.callingApplication)
+        fun adaptRemoteAnimation(
+            adapter: RemoteAnimationAdapter,
+            debugName: String
+        ): RemoteTransition {
+            return RemoteTransition(
+                adaptRemoteRunner(adapter.runner),
+                adapter.callingApplication,
+                debugName
+            )
         }
     }
 
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index e73afe7..a7e95b5 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.plugins.PluginListener
 import com.android.systemui.plugins.PluginManager
 import com.android.systemui.util.Assert
+import java.util.concurrent.ConcurrentHashMap
 import java.util.concurrent.atomic.AtomicBoolean
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
@@ -41,6 +42,18 @@
 private const val DEBUG = true
 private val KEY_TIMESTAMP = "appliedTimestamp"
 
+private fun <TKey, TVal> ConcurrentHashMap<TKey, TVal>.concurrentGetOrPut(
+    key: TKey,
+    value: TVal,
+    onNew: () -> Unit
+): TVal {
+    val result = this.putIfAbsent(key, value)
+    if (result == null) {
+        onNew()
+    }
+    return result ?: value
+}
+
 /** ClockRegistry aggregates providers and plugins */
 open class ClockRegistry(
     val context: Context,
@@ -64,7 +77,7 @@
         fun onAvailableClocksChanged() {}
     }
 
-    private val availableClocks = mutableMapOf<ClockId, ClockInfo>()
+    private val availableClocks = ConcurrentHashMap<ClockId, ClockInfo>()
     private val clockChangeListeners = mutableListOf<ClockChangeListener>()
     private val settingObserver =
         object : ContentObserver(null) {
@@ -92,18 +105,12 @@
                 var isClockListChanged = false
                 for (clock in plugin.getClocks()) {
                     val id = clock.clockId
-                    var isNew = false
                     val info =
-                        availableClocks.getOrPut(id) {
-                            isNew = true
-                            ClockInfo(clock, plugin, manager)
+                        availableClocks.concurrentGetOrPut(id, ClockInfo(clock, plugin, manager)) {
+                            isClockListChanged = true
+                            onConnected(id)
                         }
 
-                    if (isNew) {
-                        isClockListChanged = true
-                        onConnected(id)
-                    }
-
                     if (manager != info.manager) {
                         Log.e(
                             TAG,
@@ -254,10 +261,8 @@
             return
         }
 
-        android.util.Log.e("HAWK", "triggerOnCurrentClockChanged")
         scope.launch(mainDispatcher) {
             assertMainThread()
-            android.util.Log.e("HAWK", "isClockChanged")
             isClockChanged.set(false)
             clockChangeListeners.forEach { it.onCurrentClockChanged() }
         }
@@ -270,10 +275,8 @@
             return
         }
 
-        android.util.Log.e("HAWK", "triggerOnAvailableClocksChanged")
         scope.launch(mainDispatcher) {
             assertMainThread()
-            android.util.Log.e("HAWK", "isClockListChanged")
             isClockListChanged.set(false)
             clockChangeListeners.forEach { it.onAvailableClocksChanged() }
         }
@@ -356,7 +359,7 @@
     }
 
     private var isVerifying = AtomicBoolean(false)
-    private fun verifyLoadedProviders() {
+    fun verifyLoadedProviders() {
         val shouldSchedule = isVerifying.compareAndSet(false, true)
         if (!shouldSchedule) {
             return
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index b49afee..4b94707 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -28,9 +28,10 @@
     <FrameLayout
         android:id="@+id/lockscreen_clock_view"
         android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+        android:layout_height="@dimen/small_clock_height"
         android:layout_alignParentStart="true"
         android:layout_alignParentTop="true"
+        android:clipChildren="false"
         android:paddingStart="@dimen/clock_padding_start"
         android:visibility="invisible" />
     <FrameLayout
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 1f44f05..cad2c16 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -95,6 +95,7 @@
     <dimen name="num_pad_key_margin_end">12dp</dimen>
 
     <!-- additional offset for clock switch area items -->
+    <dimen name="small_clock_height">114dp</dimen>
     <dimen name="clock_padding_start">28dp</dimen>
     <dimen name="below_clock_padding_start">32dp</dimen>
     <dimen name="below_clock_padding_end">16dp</dimen>
diff --git a/packages/SystemUI/res/layout/controls_management.xml b/packages/SystemUI/res/layout/controls_management.xml
index b9e711e..d8967d4 100644
--- a/packages/SystemUI/res/layout/controls_management.xml
+++ b/packages/SystemUI/res/layout/controls_management.xml
@@ -77,6 +77,29 @@
                 app:layout_constraintStart_toStartOf="parent"/>
 
             <Button
+                android:id="@+id/rearrange"
+                android:visibility="gone"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:gravity="center_vertical"
+                style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <Button
+                android:id="@+id/addControls"
+                android:visibility="gone"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:gravity="center_vertical"
+                android:text="@string/controls_favorite_add_controls"
+                style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+                app:layout_constraintTop_toTopOf="parent"
+                app:layout_constraintBottom_toBottomOf="parent"
+                app:layout_constraintStart_toStartOf="parent"/>
+
+            <Button
                 android:id="@+id/done"
                 android:layout_width="wrap_content"
                 android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 324ba02..1dd12ee 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2471,6 +2471,15 @@
     <!-- Controls management favorites screen. See other apps button [CHAR LIMIT=30] -->
     <string name="controls_favorite_see_other_apps">See other apps</string>
 
+    <!-- Controls management favorites screen. Rearrange controls button [CHAR LIMIT=30]-->
+    <string name="controls_favorite_rearrange_button">Rearrange</string>
+
+    <!-- Controls management edit screen. Add controls button [CHAR LIMIT=30]-->
+    <string name="controls_favorite_add_controls">Add controls</string>
+
+    <!-- Controls management edit screen. Return to editing button [CHAR LIMIT=30]-->
+    <string name="controls_favorite_back_to_editing">Back to editing</string>
+
     <!-- Controls management controls screen error on load message [CHAR LIMIT=NONE] -->
     <string name="controls_favorite_load_error">Controls could not be loaded. Check the <xliff:g id="app" example="System UI">%s</xliff:g> app to make sure that the app settings haven\u2019t changed.</string>
     <!-- Controls management controls screen no controls found on load message [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index 53fab69..cab54d0 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -66,7 +66,6 @@
 
 import java.io.PrintWriter;
 import java.util.Optional;
-import java.util.function.Consumer;
 import java.util.function.Supplier;
 
 /**
@@ -244,7 +243,12 @@
 
         mListenersRegistered = false;
 
-        mContext.unregisterReceiver(mDockedReceiver);
+        try {
+            mContext.unregisterReceiver(mDockedReceiver);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Docked receiver already unregistered", e);
+        }
+
         if (mRotationWatcherRegistered) {
             try {
                 WindowManagerGlobal.getWindowManagerService().removeRotationWatcher(
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index 44f9d43..f094102 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -63,6 +63,7 @@
         final ArrayList<RemoteAnimationTarget> out = new ArrayList<>();
         for (int i = 0; i < info.getChanges().size(); i++) {
             TransitionInfo.Change change = info.getChanges().get(i);
+            if (TransitionUtil.isOrderOnly(change)) continue;
             if (filter.test(change)) {
                 out.add(TransitionUtil.newTarget(
                         change, info.getChanges().size() - i, info, t, leashMap));
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 58e7747..1fbf743 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
@@ -89,7 +89,7 @@
                 }
             }
         };
-        return new RemoteTransition(remote, appThread);
+        return new RemoteTransition(remote, appThread, "Recents");
     }
 
     /**
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
index 31234cf..c22d689 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
@@ -43,9 +43,8 @@
         id: Int,
         name: String,
         namespace: String = "systemui",
-        teamfood: Boolean = false
     ): ReleasedFlag {
-        val flag = ReleasedFlag(id = id, name = name, namespace = namespace, teamfood = teamfood)
+        val flag = ReleasedFlag(id = id, name = name, namespace = namespace, teamfood = false)
         checkForDupesAndAdd(flag)
         return flag
     }
@@ -55,7 +54,6 @@
         @BoolRes resourceId: Int,
         name: String,
         namespace: String = "systemui",
-        teamfood: Boolean = false
     ): ResourceBooleanFlag {
         val flag =
             ResourceBooleanFlag(
@@ -63,7 +61,7 @@
                 name = name,
                 namespace = namespace,
                 resourceId = resourceId,
-                teamfood = teamfood
+                teamfood = false,
             )
         checkForDupesAndAdd(flag)
         return flag
diff --git a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt
index 27c5699..5502da1 100644
--- a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt
+++ b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt
@@ -43,9 +43,8 @@
         id: Int,
         name: String,
         namespace: String = "systemui",
-        teamfood: Boolean = false
     ): ReleasedFlag {
-        val flag = ReleasedFlag(id = id, name = name, namespace = namespace, teamfood = teamfood)
+        val flag = ReleasedFlag(id = id, name = name, namespace = namespace, teamfood = false)
         flagMap[name] = flag
         return flag
     }
@@ -55,7 +54,6 @@
         @BoolRes resourceId: Int,
         name: String,
         namespace: String = "systemui",
-        teamfood: Boolean = false
     ): ResourceBooleanFlag {
         val flag =
             ResourceBooleanFlag(
@@ -63,7 +61,7 @@
                 name = name,
                 namespace = namespace,
                 resourceId = resourceId,
-                teamfood = teamfood
+                teamfood = false,
             )
         flagMap[name] = flag
         return flag
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index 9f2333d8..1980f70 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -107,14 +107,7 @@
         // start fresh
         mDismissing = false;
         mView.resetPasswordText(false /* animate */, false /* announce */);
-        // if the user is currently locked out, enforce it.
-        long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
-                KeyguardUpdateMonitor.getCurrentUser());
-        if (shouldLockout(deadline)) {
-            handleAttemptLockout(deadline);
-        } else {
-            resetState();
-        }
+        resetState();
     }
 
     @Override
@@ -277,7 +270,12 @@
     @Override
     public void onResume(int reason) {
         mResumed = true;
-        reset();
+        // if the user is currently locked out, enforce it.
+        long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
+                KeyguardUpdateMonitor.getCurrentUser());
+        if (shouldLockout(deadline)) {
+            handleAttemptLockout(deadline);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index cdaed87..07333f7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -390,6 +390,13 @@
             PropertyAnimator.setProperty(mStatusArea, AnimatableProperty.TRANSLATION_X,
                     x, props, animate);
         }
+
+    }
+
+    void updateKeyguardStatusViewOffset() {
+        // updateClockTargetRegions will call onTargetRegionChanged
+        // which will require the correct translationY property of keyguardStatusView after updating
+        mView.updateClockTargetRegions();
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 68b40ab..5c56aab 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -260,20 +260,18 @@
         mLockPatternView.setEnabled(true);
         mLockPatternView.clearPattern();
 
-        // if the user is currently locked out, enforce it.
-        long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
-                KeyguardUpdateMonitor.getCurrentUser());
-        if (deadline != 0) {
-            handleAttemptLockout(deadline);
-        } else {
-            displayDefaultSecurityMessage();
-        }
+        displayDefaultSecurityMessage();
     }
 
     @Override
     public void onResume(int reason) {
         super.onResume(reason);
-        reset();
+        // if the user is currently locked out, enforce it.
+        long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
+                KeyguardUpdateMonitor.getCurrentUser());
+        if (deadline != 0) {
+            handleAttemptLockout(deadline);
+        }
     }
 
     @Override
@@ -300,34 +298,38 @@
     @Override
     public void showPromptReason(int reason) {
         /// TODO: move all this logic into the MessageAreaController?
+        int resId =  0;
         switch (reason) {
             case PROMPT_REASON_RESTART:
-                mMessageAreaController.setMessage(R.string.kg_prompt_reason_restart_pattern);
+                resId = R.string.kg_prompt_reason_restart_pattern;
                 break;
             case PROMPT_REASON_TIMEOUT:
-                mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+                resId = R.string.kg_prompt_reason_timeout_pattern;
                 break;
             case PROMPT_REASON_DEVICE_ADMIN:
-                mMessageAreaController.setMessage(R.string.kg_prompt_reason_device_admin);
+                resId = R.string.kg_prompt_reason_device_admin;
                 break;
             case PROMPT_REASON_USER_REQUEST:
-                mMessageAreaController.setMessage(R.string.kg_prompt_reason_user_request);
+                resId = R.string.kg_prompt_reason_user_request;
                 break;
             case PROMPT_REASON_PREPARE_FOR_UPDATE:
-                mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+                resId = R.string.kg_prompt_reason_timeout_pattern;
                 break;
             case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
-                mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+                resId = R.string.kg_prompt_reason_timeout_pattern;
                 break;
             case PROMPT_REASON_TRUSTAGENT_EXPIRED:
-                mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+                resId = R.string.kg_prompt_reason_timeout_pattern;
                 break;
             case PROMPT_REASON_NONE:
                 break;
             default:
-                mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+                resId = R.string.kg_prompt_reason_timeout_pattern;
                 break;
         }
+        if (resId != 0) {
+            mMessageAreaController.setMessage(getResources().getText(resId), /* animate= */ false);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 67874e1..87a7758 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -40,6 +40,7 @@
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.MathUtils;
 import android.util.Slog;
@@ -64,6 +65,7 @@
 import com.android.keyguard.KeyguardSecurityContainer.SwipeListener;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.keyguard.dagger.KeyguardBouncerScope;
+import com.android.settingslib.Utils;
 import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
@@ -634,6 +636,16 @@
                 mKeyguardStateController.isFaceAuthEnabled());
     }
 
+    /** Sets an initial message that would override the default message */
+    public void setInitialMessage() {
+        CharSequence customMessage = mViewMediatorCallback.consumeCustomMessage();
+        if (!TextUtils.isEmpty(customMessage)) {
+            showMessage(customMessage, Utils.getColorError(getContext()));
+            return;
+        }
+        showPromptReason(mViewMediatorCallback.getBouncerPromptReason());
+    }
+
     /**
      * Show the bouncer and start appear animations.
      *
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index f4c5815..fd55d69 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -172,11 +172,15 @@
      * Update position of the view with an optional animation
      */
     public void updatePosition(int x, int y, float scale, boolean animate) {
+        float oldY = mView.getY();
         PropertyAnimator.setProperty(mView, AnimatableProperty.Y, y, CLOCK_ANIMATION_PROPERTIES,
                 animate);
 
         mKeyguardClockSwitchController.updatePosition(x, scale, CLOCK_ANIMATION_PROPERTIES,
                 animate);
+        if (oldY != y) {
+            mKeyguardClockSwitchController.updateKeyguardStatusViewOffset();
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index a678edc..ac0a3fd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -28,6 +28,7 @@
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
+import com.android.systemui.statusbar.phone.AnimatorHandle;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -47,6 +48,7 @@
     private final ScreenOffAnimationController mScreenOffAnimationController;
     private boolean mAnimateYPos;
     private boolean mKeyguardViewVisibilityAnimating;
+    private AnimatorHandle mKeyguardAnimatorHandle;
     private boolean mLastOccludedState = false;
     private final AnimationProperties mAnimationProperties = new AnimationProperties();
     private final LogBuffer mLogBuffer;
@@ -83,6 +85,10 @@
             boolean keyguardFadingAway,
             boolean goingToFullShade,
             int oldStatusBarState) {
+        if (mKeyguardAnimatorHandle != null) {
+            mKeyguardAnimatorHandle.cancel();
+            mKeyguardAnimatorHandle = null;
+        }
         mView.animate().cancel();
         boolean isOccluded = mKeyguardStateController.isOccluded();
         mKeyguardViewVisibilityAnimating = false;
@@ -116,7 +122,7 @@
                     .setDuration(320)
                     .setInterpolator(Interpolators.ALPHA_IN)
                     .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable);
-            log("keyguardFadingAway transition w/ Y Aniamtion");
+            log("keyguardFadingAway transition w/ Y Animation");
         } else if (statusBarState == KEYGUARD) {
             if (keyguardFadingAway) {
                 mKeyguardViewVisibilityAnimating = true;
@@ -148,7 +154,7 @@
 
                 // Ask the screen off animation controller to animate the keyguard visibility for us
                 // since it may need to be cancelled due to keyguard lifecycle events.
-                mScreenOffAnimationController.animateInKeyguard(
+                mKeyguardAnimatorHandle = mScreenOffAnimationController.animateInKeyguard(
                         mView, mAnimateKeyguardStatusViewVisibleEndRunnable);
             } else {
                 log("Direct set Visibility to VISIBLE");
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 1ae380e..235a8bc 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -22,7 +22,6 @@
 import static com.android.keyguard.LockIconView.ICON_FINGERPRINT;
 import static com.android.keyguard.LockIconView.ICON_LOCK;
 import static com.android.keyguard.LockIconView.ICON_UNLOCK;
-import static com.android.systemui.classifier.Classifier.LOCK_ICON;
 import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
 import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
 import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
@@ -127,8 +126,6 @@
     private boolean mCanDismissLockScreen;
     private int mStatusBarState;
     private boolean mIsKeyguardShowing;
-    private boolean mUserUnlockedWithBiometric;
-    private Runnable mCancelDelayedUpdateVisibilityRunnable;
     private Runnable mOnGestureDetectedRunnable;
     private Runnable mLongPressCancelRunnable;
 
@@ -229,7 +226,6 @@
         updateIsUdfpsEnrolled();
         updateConfiguration();
         updateKeyguardShowing();
-        mUserUnlockedWithBiometric = false;
 
         mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
         mIsDozing = mStatusBarStateController.isDozing();
@@ -270,11 +266,6 @@
         mStatusBarStateController.removeCallback(mStatusBarStateListener);
         mKeyguardStateController.removeCallback(mKeyguardStateCallback);
 
-        if (mCancelDelayedUpdateVisibilityRunnable != null) {
-            mCancelDelayedUpdateVisibilityRunnable.run();
-            mCancelDelayedUpdateVisibilityRunnable = null;
-        }
-
         mAccessibilityManager.removeAccessibilityStateChangeListener(
                 mAccessibilityStateChangeListener);
     }
@@ -288,11 +279,6 @@
     }
 
     private void updateVisibility() {
-        if (mCancelDelayedUpdateVisibilityRunnable != null) {
-            mCancelDelayedUpdateVisibilityRunnable.run();
-            mCancelDelayedUpdateVisibilityRunnable = null;
-        }
-
         if (!mIsKeyguardShowing && !mIsDozing) {
             mView.setVisibility(View.INVISIBLE);
             return;
@@ -300,9 +286,9 @@
 
         boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon
                 && !mShowAodUnlockedIcon && !mShowAodLockIcon;
-        mShowLockIcon = !mCanDismissLockScreen && !mUserUnlockedWithBiometric && isLockScreen()
+        mShowLockIcon = !mCanDismissLockScreen && isLockScreen()
                 && (!mUdfpsEnrolled || !mRunningFPS);
-        mShowUnlockIcon = (mCanDismissLockScreen || mUserUnlockedWithBiometric) && isLockScreen();
+        mShowUnlockIcon = mCanDismissLockScreen && isLockScreen();
         mShowAodUnlockedIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && mCanDismissLockScreen;
         mShowAodLockIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && !mCanDismissLockScreen;
 
@@ -426,7 +412,6 @@
         pw.println(" isFlagEnabled(DOZING_MIGRATION_1): "
                 + mFeatureFlags.isEnabled(DOZING_MIGRATION_1));
         pw.println(" mIsBouncerShowing: " + mIsBouncerShowing);
-        pw.println(" mUserUnlockedWithBiometric: " + mUserUnlockedWithBiometric);
         pw.println(" mRunningFPS: " + mRunningFPS);
         pw.println(" mCanDismissLockScreen: " + mCanDismissLockScreen);
         pw.println(" mStatusBarState: " + StatusBarState.toString(mStatusBarState));
@@ -469,17 +454,6 @@
         }
     }
 
-    /**
-     * @return whether the userUnlockedWithBiometric state changed
-     */
-    private boolean updateUserUnlockedWithBiometric() {
-        final boolean wasUserUnlockedWithBiometric = mUserUnlockedWithBiometric;
-        mUserUnlockedWithBiometric =
-                mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(
-                        KeyguardUpdateMonitor.getCurrentUser());
-        return wasUserUnlockedWithBiometric != mUserUnlockedWithBiometric;
-    }
-
     private StatusBarStateController.StateListener mStatusBarStateListener =
             new StatusBarStateController.StateListener() {
                 @Override
@@ -516,36 +490,15 @@
                 }
 
                 @Override
-                public void onBiometricsCleared() {
-                    if (updateUserUnlockedWithBiometric()) {
-                        updateVisibility();
-                    }
-                }
-
-                @Override
                 public void onBiometricRunningStateChanged(boolean running,
                         BiometricSourceType biometricSourceType) {
                     final boolean wasRunningFps = mRunningFPS;
-                    final boolean userUnlockedWithBiometricChanged =
-                            updateUserUnlockedWithBiometric();
 
                     if (biometricSourceType == FINGERPRINT) {
                         mRunningFPS = running;
-                        if (wasRunningFps && !mRunningFPS) {
-                            if (mCancelDelayedUpdateVisibilityRunnable != null) {
-                                mCancelDelayedUpdateVisibilityRunnable.run();
-                            }
-
-                            // For some devices, auth is cancelled immediately on screen off but
-                            // before dozing state is set. We want to avoid briefly showing the
-                            // button in this case, so we delay updating the visibility by 50ms.
-                            mCancelDelayedUpdateVisibilityRunnable =
-                                    mExecutor.executeDelayed(() -> updateVisibility(), 50);
-                            return;
-                        }
                     }
 
-                    if (userUnlockedWithBiometricChanged || wasRunningFps != mRunningFPS) {
+                    if (wasRunningFps != mRunningFPS) {
                         updateVisibility();
                     }
                 }
@@ -556,7 +509,6 @@
         @Override
         public void onUnlockedChanged() {
             mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
-            updateUserUnlockedWithBiometric();
             updateKeyguardShowing();
             updateVisibility();
         }
@@ -573,9 +525,6 @@
             mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
 
             updateKeyguardShowing();
-            if (mIsKeyguardShowing) {
-                updateUserUnlockedWithBiometric();
-            }
             updateVisibility();
         }
 
@@ -694,7 +643,7 @@
 
     private void onLongPress() {
         cancelTouches();
-        if (mFalsingManager.isFalseTouch(LOCK_ICON)) {
+        if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
             Log.v(TAG, "lock icon long-press rejected by the falsing manager.");
             return;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index cbc0a1b..8e5c76c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -25,7 +25,6 @@
 import static android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR;
 
 import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.systemui.classifier.Classifier.LOCK_ICON;
 import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
 
 import android.content.BroadcastReceiver;
@@ -983,7 +982,7 @@
         }
 
         if (!mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
-            if (mFalsingManager.isFalseTouch(LOCK_ICON)) {
+            if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
                 Log.v(TAG, "aod lock icon long-press rejected by the falsing manager.");
                 return;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
index 701df89..334cf93 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
@@ -41,7 +41,6 @@
     public static final int SHADE_DRAG = 11;
     public static final int QS_COLLAPSE = 12;
     public static final int UDFPS_AUTHENTICATION = 13;
-    public static final int LOCK_ICON = 14;
     public static final int QS_SWIPE_SIDE = 15;
     public static final int BACK_GESTURE = 16;
     public static final int QS_SWIPE_NESTED = 17;
@@ -58,12 +57,10 @@
             GENERIC,
             BOUNCER_UNLOCK,
             PULSE_EXPAND,
-            BRIGHTNESS_SLIDER,
             SHADE_DRAG,
             QS_COLLAPSE,
             BRIGHTNESS_SLIDER,
             UDFPS_AUTHENTICATION,
-            LOCK_ICON,
             QS_SWIPE_SIDE,
             QS_SWIPE_NESTED,
             BACK_GESTURE,
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
index d17eadd..8ec48b9 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
@@ -19,7 +19,6 @@
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_HORIZONTAL_ANGLE_RANGE;
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_VERTICAL_ANGLE_RANGE;
 import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE;
-import static com.android.systemui.classifier.Classifier.LOCK_ICON;
 import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE;
 
 import android.provider.DeviceConfig;
@@ -73,8 +72,7 @@
         }
 
         if (interactionType == LEFT_AFFORDANCE
-                || interactionType == RIGHT_AFFORDANCE
-                || interactionType == LOCK_ICON) {
+                || interactionType == RIGHT_AFFORDANCE) {
             return Result.passed(0);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
index f8ee49a..15e2e9a 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
@@ -158,7 +158,6 @@
                 || interactionType == SHADE_DRAG
                 || interactionType == QS_COLLAPSE
                 || interactionType == Classifier.UDFPS_AUTHENTICATION
-                || interactionType == Classifier.LOCK_ICON
                 || interactionType == Classifier.QS_SWIPE_SIDE
                 || interactionType == QS_SWIPE_NESTED) {
             return Result.passed(0);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
index d8d2c98..2fb6aaf 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
@@ -47,8 +47,7 @@
     Result calculateFalsingResult(
             @Classifier.InteractionType int interactionType,
             double historyBelief, double historyConfidence) {
-        if (interactionType == Classifier.UDFPS_AUTHENTICATION
-                || interactionType == Classifier.LOCK_ICON) {
+        if (interactionType == Classifier.UDFPS_AUTHENTICATION) {
             return Result.passed(0);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
index 840982c..4a3710b 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
@@ -21,7 +21,6 @@
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_PRIMARY_DEVIANCE;
 import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_SECONDARY_DEVIANCE;
 import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
-import static com.android.systemui.classifier.Classifier.LOCK_ICON;
 import static com.android.systemui.classifier.Classifier.MEDIA_SEEKBAR;
 import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
 
@@ -93,8 +92,7 @@
             double historyBelief, double historyConfidence) {
         if (interactionType == BRIGHTNESS_SLIDER
                 || interactionType == MEDIA_SEEKBAR
-                || interactionType == SHADE_DRAG
-                || interactionType == LOCK_ICON) {
+                || interactionType == SHADE_DRAG) {
             return Result.passed(0);
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index e049ae0..c312f69 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -308,7 +308,7 @@
                 if (model.isSensitive()) {
                     mView.showTextPreview(mContext.getString(R.string.clipboard_asterisks), true);
                 } else {
-                    mView.showTextPreview(model.getText(), false);
+                    mView.showTextPreview(model.getText().toString(), false);
                 }
                 mView.setEditAccessibilityAction(true);
                 mOnPreviewTapped = this::editText;
@@ -527,7 +527,7 @@
     }
 
     private void showEditableText(CharSequence text, boolean hidden) {
-        mView.showTextPreview(text, hidden);
+        mView.showTextPreview(text.toString(), hidden);
         mView.setEditAccessibilityAction(true);
         mOnPreviewTapped = this::editText;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index ac1150e..e8c97bf 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -31,7 +31,6 @@
 import android.util.ArrayMap
 import android.util.Log
 import com.android.internal.annotations.VisibleForTesting
-import com.android.internal.notification.NotificationAccessConfirmationActivityContract.EXTRA_USER_ID
 import com.android.systemui.Dumpable
 import com.android.systemui.backup.BackupHelper
 import com.android.systemui.controls.ControlStatus
@@ -44,7 +43,6 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.people.widget.PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE
 import com.android.systemui.settings.UserFileManager
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.PREFS_CONTROLS_FILE
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
index 00a406e..be428a8 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
@@ -75,9 +75,12 @@
         } else {
             favoriteIds.remove(controlId)
         }
-        if (changed && !modified) {
-            modified = true
-            controlsModelCallback.onFirstChange()
+        if (changed) {
+            if (!modified) {
+                modified = true
+                controlsModelCallback.onFirstChange()
+            }
+            controlsModelCallback.onChange()
         }
         toChange?.let {
             it.controlStatus.favorite = favorite
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
index 7df0865..d629e3e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
@@ -27,6 +27,7 @@
 import android.view.ViewStub
 import android.widget.Button
 import android.widget.TextView
+import android.widget.Toast
 import android.window.OnBackInvokedCallback
 import android.window.OnBackInvokedDispatcher
 import androidx.activity.ComponentActivity
@@ -38,8 +39,9 @@
 import com.android.systemui.controls.controller.ControlsControllerImpl
 import com.android.systemui.controls.controller.StructureInfo
 import com.android.systemui.controls.ui.ControlsActivity
-import com.android.systemui.controls.ui.ControlsUiController
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.settings.UserTracker
 import java.util.concurrent.Executor
 import javax.inject.Inject
@@ -48,17 +50,19 @@
  * Activity for rearranging and removing controls for a given structure
  */
 open class ControlsEditingActivity @Inject constructor(
+    featureFlags: FeatureFlags,
     @Main private val mainExecutor: Executor,
     private val controller: ControlsControllerImpl,
     private val userTracker: UserTracker,
     private val customIconCache: CustomIconCache,
-    private val uiController: ControlsUiController
 ) : ComponentActivity() {
 
     companion object {
         private const val DEBUG = false
         private const val TAG = "ControlsEditingActivity"
         const val EXTRA_STRUCTURE = ControlsFavoritingActivity.EXTRA_STRUCTURE
+        const val EXTRA_APP = ControlsFavoritingActivity.EXTRA_APP
+        const val EXTRA_FROM_FAVORITING = "extra_from_favoriting"
         private val SUBTITLE_ID = R.string.controls_favorite_rearrange
         private val EMPTY_TEXT_ID = R.string.controls_favorite_removed
     }
@@ -68,7 +72,12 @@
     private lateinit var model: FavoritesModel
     private lateinit var subtitle: TextView
     private lateinit var saveButton: View
+    private lateinit var addControls: View
 
+    private var isFromFavoriting: Boolean = false
+
+    private val isNewFlowEnabled: Boolean =
+        featureFlags.isEnabled(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS)
     private val userTrackerCallback: UserTracker.Callback = object : UserTracker.Callback {
         private val startingUser = controller.currentUserId
 
@@ -93,7 +102,7 @@
         intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)?.let {
             component = it
         } ?: run(this::finish)
-
+        isFromFavoriting = intent.getBooleanExtra(EXTRA_FROM_FAVORITING, false)
         intent.getCharSequenceExtra(EXTRA_STRUCTURE)?.let {
             structure = it
         } ?: run(this::finish)
@@ -165,8 +174,42 @@
     }
 
     private fun bindButtons() {
+        addControls = requireViewById<Button>(R.id.addControls).apply {
+            isEnabled = true
+            visibility = if (isNewFlowEnabled) View.VISIBLE else View.GONE
+            setOnClickListener {
+                if (saveButton.isEnabled) {
+                    // The user has made changes
+                    Toast.makeText(
+                        applicationContext,
+                        R.string.controls_favorite_toast_no_changes,
+                        Toast.LENGTH_SHORT
+                    ).show()
+                }
+                if (isFromFavoriting) {
+                    animateExitAndFinish()
+                } else {
+                    startActivity(Intent(context, ControlsFavoritingActivity::class.java).also {
+                        it.putExtra(ControlsFavoritingActivity.EXTRA_STRUCTURE, structure)
+                        it.putExtra(Intent.EXTRA_COMPONENT_NAME, component)
+                        it.putExtra(
+                            ControlsFavoritingActivity.EXTRA_APP,
+                            intent.getCharSequenceExtra(EXTRA_APP),
+                        )
+                        it.putExtra(
+                            ControlsFavoritingActivity.EXTRA_SOURCE,
+                            ControlsFavoritingActivity.EXTRA_SOURCE_VALUE_FROM_EDITING,
+                        )
+                    },
+                                  ActivityOptions.makeSceneTransitionAnimation(
+                                      this@ControlsEditingActivity
+                                  ).toBundle(),
+                    )
+                }
+            }
+        }
         saveButton = requireViewById<Button>(R.id.done).apply {
-            isEnabled = false
+            isEnabled = isFromFavoriting
             setText(R.string.save)
             setOnClickListener {
                 saveFavorites()
@@ -194,6 +237,8 @@
             }
         }
 
+        override fun onChange() = Unit
+
         override fun onFirstChange() {
             saveButton.isEnabled = true
         }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index 3e97d31..d3ffc95 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -37,6 +37,7 @@
 import android.window.OnBackInvokedCallback
 import android.window.OnBackInvokedDispatcher
 import androidx.activity.ComponentActivity
+import androidx.annotation.VisibleForTesting
 import androidx.viewpager2.widget.ViewPager2
 import com.android.systemui.Prefs
 import com.android.systemui.R
@@ -45,20 +46,20 @@
 import com.android.systemui.controls.controller.ControlsControllerImpl
 import com.android.systemui.controls.controller.StructureInfo
 import com.android.systemui.controls.ui.ControlsActivity
-import com.android.systemui.controls.ui.ControlsUiController
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.settings.UserTracker
 import java.text.Collator
 import java.util.concurrent.Executor
-import java.util.function.Consumer
 import javax.inject.Inject
 
 open class ControlsFavoritingActivity @Inject constructor(
+    featureFlags: FeatureFlags,
     @Main private val executor: Executor,
     private val controller: ControlsControllerImpl,
     private val listingController: ControlsListingController,
     private val userTracker: UserTracker,
-    private val uiController: ControlsUiController
 ) : ComponentActivity() {
 
     companion object {
@@ -71,7 +72,10 @@
         // If provided, show this structure page first
         const val EXTRA_STRUCTURE = "extra_structure"
         const val EXTRA_SINGLE_STRUCTURE = "extra_single_structure"
-        const val EXTRA_FROM_PROVIDER_SELECTOR = "extra_from_provider_selector"
+        const val EXTRA_SOURCE = "extra_source"
+        const val EXTRA_SOURCE_UNDEFINED: Byte = 0
+        const val EXTRA_SOURCE_VALUE_FROM_PROVIDER_SELECTOR: Byte = 1
+        const val EXTRA_SOURCE_VALUE_FROM_EDITING: Byte = 2
         private const val TOOLTIP_PREFS_KEY = Prefs.Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT
         private const val TOOLTIP_MAX_SHOWN = 2
     }
@@ -79,7 +83,7 @@
     private var component: ComponentName? = null
     private var appName: CharSequence? = null
     private var structureExtra: CharSequence? = null
-    private var fromProviderSelector = false
+    private var openSource = EXTRA_SOURCE_UNDEFINED
 
     private lateinit var structurePager: ViewPager2
     private lateinit var statusText: TextView
@@ -89,12 +93,19 @@
     private var mTooltipManager: TooltipManager? = null
     private lateinit var doneButton: View
     private lateinit var otherAppsButton: View
+    private lateinit var rearrangeButton: Button
     private var listOfStructures = emptyList<StructureContainer>()
 
     private lateinit var comparator: Comparator<StructureContainer>
     private var cancelLoadRunnable: Runnable? = null
     private var isPagerLoaded = false
 
+    private val fromProviderSelector: Boolean
+        get() = openSource == EXTRA_SOURCE_VALUE_FROM_PROVIDER_SELECTOR
+    private val fromEditing: Boolean
+        get() = openSource == EXTRA_SOURCE_VALUE_FROM_EDITING
+    private val isNewFlowEnabled: Boolean =
+        featureFlags.isEnabled(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS)
     private val userTrackerCallback: UserTracker.Callback = object : UserTracker.Callback {
         private val startingUser = controller.currentUserId
 
@@ -117,14 +128,20 @@
 
         override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
             if (serviceInfos.size > 1) {
-                otherAppsButton.post {
-                    otherAppsButton.visibility = View.VISIBLE
+                val newVisibility = if (isNewFlowEnabled) View.GONE else View.VISIBLE
+                if (otherAppsButton.visibility != newVisibility) {
+                    otherAppsButton.post {
+                        otherAppsButton.visibility = newVisibility
+                    }
                 }
             }
         }
     }
 
     override fun onBackPressed() {
+        if (fromEditing) {
+            animateExitAndFinish()
+        }
         if (!fromProviderSelector) {
             openControlsOrigin()
         }
@@ -139,7 +156,7 @@
         appName = intent.getCharSequenceExtra(EXTRA_APP)
         structureExtra = intent.getCharSequenceExtra(EXTRA_STRUCTURE)
         component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)
-        fromProviderSelector = intent.getBooleanExtra(EXTRA_FROM_PROVIDER_SELECTOR, false)
+        openSource = intent.getByteExtra(EXTRA_SOURCE, EXTRA_SOURCE_UNDEFINED)
 
         bindViews()
     }
@@ -148,14 +165,19 @@
         override fun onFirstChange() {
             doneButton.isEnabled = true
         }
+
+        override fun onChange() {
+            val structure: StructureContainer = listOfStructures[structurePager.currentItem]
+            rearrangeButton.isEnabled = structure.model.favorites.isNotEmpty()
+        }
     }
 
     private fun loadControls() {
-        component?.let {
+        component?.let { componentName ->
             statusText.text = resources.getText(com.android.internal.R.string.loading)
             val emptyZoneString = resources.getText(
                     R.string.controls_favorite_other_zone_header)
-            controller.loadForComponent(it, Consumer { data ->
+            controller.loadForComponent(componentName, { data ->
                 val allControls = data.allControls
                 val favoriteKeys = data.favoritesIds
                 val error = data.errorOnLoad
@@ -213,7 +235,7 @@
                         ControlsAnimations.enterAnimation(structurePager).start()
                     }
                 }
-            }, Consumer { runnable -> cancelLoadRunnable = runnable })
+            }, { runnable -> cancelLoadRunnable = runnable })
         }
     }
 
@@ -299,7 +321,8 @@
         bindButtons()
     }
 
-    private fun animateExitAndFinish() {
+    @VisibleForTesting
+    internal open fun animateExitAndFinish() {
         val rootView = requireViewById<ViewGroup>(R.id.controls_management_root)
         ControlsAnimations.exitAnimation(
                 rootView,
@@ -312,6 +335,32 @@
     }
 
     private fun bindButtons() {
+        rearrangeButton = requireViewById<Button>(R.id.rearrange).apply {
+            text = if (fromEditing) {
+                getString(R.string.controls_favorite_back_to_editing)
+            } else {
+                getString(R.string.controls_favorite_rearrange_button)
+            }
+            isEnabled = false
+            visibility = if (isNewFlowEnabled) View.VISIBLE else View.GONE
+            setOnClickListener {
+                if (component == null) return@setOnClickListener
+                saveFavorites()
+                startActivity(
+                    Intent(context, ControlsEditingActivity::class.java).also {
+                        it.putExtra(Intent.EXTRA_COMPONENT_NAME, component)
+                        it.putExtra(ControlsEditingActivity.EXTRA_APP, appName)
+                        it.putExtra(ControlsEditingActivity.EXTRA_FROM_FAVORITING, true)
+                        it.putExtra(
+                            ControlsEditingActivity.EXTRA_STRUCTURE,
+                            listOfStructures[structurePager.currentItem].structureName,
+                        )
+                    },
+                    ActivityOptions
+                        .makeSceneTransitionAnimation(this@ControlsFavoritingActivity).toBundle()
+                )
+            }
+        }
         otherAppsButton = requireViewById<Button>(R.id.other_apps).apply {
             setOnClickListener {
                 if (doneButton.isEnabled) {
@@ -335,18 +384,22 @@
             isEnabled = false
             setOnClickListener {
                 if (component == null) return@setOnClickListener
-                listOfStructures.forEach {
-                    val favoritesForStorage = it.model.favorites
-                    controller.replaceFavoritesForStructure(
-                        StructureInfo(component!!, it.structureName, favoritesForStorage)
-                    )
-                }
+                saveFavorites()
                 animateExitAndFinish()
                 openControlsOrigin()
             }
         }
     }
 
+    private fun saveFavorites() {
+        listOfStructures.forEach {
+            val favoritesForStorage = it.model.favorites
+            controller.replaceFavoritesForStructure(
+                StructureInfo(component!!, it.structureName, favoritesForStorage)
+            )
+        }
+    }
+
     private fun openControlsOrigin() {
         startActivity(
             Intent(applicationContext, ControlsActivity::class.java),
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
index d65481a..3455e6d 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
@@ -71,6 +71,11 @@
          * Use to notify that the model has changed for the first time
          */
         fun onFirstChange()
+
+        /**
+         * Use to notify that the model has changed
+         */
+        fun onChange()
     }
 
     /**
@@ -132,7 +137,7 @@
         controlInfo: ControlInfo,
         favorite: Boolean,
         customIconGetter: (ComponentName, String) -> Icon?
-    ): this(component, controlInfo, favorite) {
+    ) : this(component, controlInfo, favorite) {
         this.customIconGetter = customIconGetter
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index 3808e73..92aff06 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -211,7 +211,10 @@
                     putExtra(ControlsFavoritingActivity.EXTRA_APP,
                             listingController.getAppLabel(it))
                     putExtra(Intent.EXTRA_COMPONENT_NAME, it)
-                    putExtra(ControlsFavoritingActivity.EXTRA_FROM_PROVIDER_SELECTOR, true)
+                    putExtra(
+                        ControlsFavoritingActivity.EXTRA_SOURCE,
+                        ControlsFavoritingActivity.EXTRA_SOURCE_VALUE_FROM_PROVIDER_SELECTOR,
+                    )
                 }
                 startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
                 animateExitAndFinish()
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index 74a49a8..c954f98 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -201,8 +201,6 @@
         mStatusBarItemsProvider.addCallback(mStatusBarItemsProviderCallback);
 
         mDreamOverlayStateController.addCallback(mDreamOverlayStateCallback);
-
-        mTouchInsetSession.addViewToTracking(mView);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
index 43e4c62..7f44463 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
@@ -101,6 +101,10 @@
 
                     completer.set(predecessor);
                 }
+
+                if (mActiveTouchSessions.isEmpty() && mStopMonitoringPending) {
+                    stopMonitoring(false);
+                }
             });
 
             return "DreamOverlayTouchMonitor::pop";
@@ -214,7 +218,12 @@
 
         @Override
         public void onPause(@NonNull LifecycleOwner owner) {
-            stopMonitoring();
+            stopMonitoring(false);
+        }
+
+        @Override
+        public void onDestroy(LifecycleOwner owner) {
+            stopMonitoring(true);
         }
     };
 
@@ -222,7 +231,7 @@
      * When invoked, instantiates a new {@link InputSession} to monitor touch events.
      */
     private void startMonitoring() {
-        stopMonitoring();
+        stopMonitoring(true);
         mCurrentInputSession = mInputSessionFactory.create(
                 "dreamOverlay",
                 mInputEventListener,
@@ -234,11 +243,16 @@
     /**
      * Destroys any active {@link InputSession}.
      */
-    private void stopMonitoring() {
+    private void stopMonitoring(boolean force) {
         if (mCurrentInputSession == null) {
             return;
         }
 
+        if (!mActiveTouchSessions.isEmpty() && !force) {
+            mStopMonitoringPending = true;
+            return;
+        }
+
         // When we stop monitoring touches, we must ensure that all active touch sessions and
         // descendants informed of the removal so any cleanup for active tracking can proceed.
         mExecutor.execute(() -> mActiveTouchSessions.forEach(touchSession -> {
@@ -250,6 +264,7 @@
 
         mCurrentInputSession.dispose();
         mCurrentInputSession = null;
+        mStopMonitoringPending = false;
     }
 
 
@@ -257,6 +272,8 @@
     private final Collection<DreamTouchHandler> mHandlers;
     private final DisplayHelper mDisplayHelper;
 
+    private boolean mStopMonitoringPending;
+
     private InputChannelCompat.InputEventListener mInputEventListener =
             new InputChannelCompat.InputEventListener() {
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/ShadeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/ShadeTouchHandler.java
new file mode 100644
index 0000000..58b70b0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/ShadeTouchHandler.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.touch;
+
+import static com.android.systemui.dreams.touch.dagger.ShadeModule.NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT;
+
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
+
+import java.util.Optional;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * {@link ShadeTouchHandler} is responsible for handling swipe down gestures over dream
+ * to bring down the shade.
+ */
+public class ShadeTouchHandler implements DreamTouchHandler {
+    private final Optional<CentralSurfaces> mSurfaces;
+    private final int mInitiationHeight;
+
+    @Inject
+    ShadeTouchHandler(Optional<CentralSurfaces> centralSurfaces,
+            @Named(NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT) int initiationHeight) {
+        mSurfaces = centralSurfaces;
+        mInitiationHeight = initiationHeight;
+    }
+
+    @Override
+    public void onSessionStart(TouchSession session) {
+        if (mSurfaces.map(CentralSurfaces::isBouncerShowing).orElse(false)) {
+            session.pop();
+            return;
+        }
+
+        session.registerInputListener(ev -> {
+            final NotificationPanelViewController viewController =
+                    mSurfaces.map(CentralSurfaces::getNotificationPanelViewController).orElse(null);
+
+            if (viewController != null) {
+                viewController.handleExternalTouch((MotionEvent) ev);
+            }
+
+            if (ev instanceof MotionEvent) {
+                if (((MotionEvent) ev).getAction() == MotionEvent.ACTION_UP) {
+                    session.pop();
+                }
+            }
+        });
+
+        session.registerGestureListener(new GestureDetector.SimpleOnGestureListener() {
+            @Override
+            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+                    float distanceY) {
+                return true;
+            }
+
+            @Override
+            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+                    float velocityY) {
+                return true;
+            }
+        });
+    }
+
+    @Override
+    public void getTouchInitiationRegion(Rect bounds, Region region) {
+        final Rect outBounds = new Rect(bounds);
+        outBounds.inset(0, 0, 0, outBounds.height() - mInitiationHeight);
+        region.op(outBounds, Region.Op.UNION);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java
index dad0004..b719126 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java
@@ -23,6 +23,7 @@
  */
 @Module(includes = {
             BouncerSwipeModule.class,
+            ShadeModule.class,
         }, subcomponents = {
             InputSessionComponent.class,
 })
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/ShadeModule.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/ShadeModule.java
new file mode 100644
index 0000000..9e0ae41
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/ShadeModule.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.touch.dagger;
+
+import android.content.res.Resources;
+
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.touch.DreamTouchHandler;
+import com.android.systemui.dreams.touch.ShadeTouchHandler;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoSet;
+
+import javax.inject.Named;
+
+/**
+ * Dependencies for swipe down to notification over dream.
+ */
+@Module
+public class ShadeModule {
+    /**
+     * The height, defined in pixels, of the gesture initiation region at the top of the screen for
+     * swiping down notifications.
+     */
+    public static final String NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT =
+            "notification_shade_gesture_initiation_height";
+
+    /**
+     * Provides {@link ShadeTouchHandler} to handle notification swipe down over dream.
+     */
+    @Provides
+    @IntoSet
+    public static DreamTouchHandler providesNotificationShadeTouchHandler(
+            ShadeTouchHandler touchHandler) {
+        return touchHandler;
+    }
+
+    /**
+     * Provides the height of the gesture area for notification swipe down.
+     */
+    @Provides
+    @Named(NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT)
+    public static int providesNotificationShadeGestureRegionHeight(@Main Resources resources) {
+        return resources.getDimensionPixelSize(R.dimen.dream_overlay_status_bar_height);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 27f35db..43221a7 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -101,12 +101,12 @@
         releasedFlag(174148361, "notification_inline_reply_animation")
 
     val FILTER_UNSEEN_NOTIFS_ON_KEYGUARD =
-        releasedFlag(254647461, "filter_unseen_notifs_on_keyguard", teamfood = true)
+        releasedFlag(254647461, "filter_unseen_notifs_on_keyguard")
 
     // TODO(b/263414400): Tracking Bug
     @JvmField
     val NOTIFICATION_ANIMATE_BIG_PICTURE =
-        releasedFlag(120, "notification_animate_big_picture", teamfood = true)
+        releasedFlag(120, "notification_animate_big_picture")
 
     @JvmField
     val ANIMATED_NOTIFICATION_SHADE_INSETS =
@@ -184,7 +184,7 @@
     // flag for controlling auto pin confirmation and material u shapes in bouncer
     @JvmField
     val AUTO_PIN_CONFIRMATION =
-        releasedFlag(224, "auto_pin_confirmation", "auto_pin_confirmation", teamfood = true)
+        releasedFlag(224, "auto_pin_confirmation", "auto_pin_confirmation")
 
     // TODO(b/262859270): Tracking Bug
     @JvmField val FALSING_OFF_FOR_UNFOLDED = releasedFlag(225, "falsing_off_for_unfolded")
@@ -621,15 +621,15 @@
     @JvmField val NOTE_TASKS = releasedFlag(1900, "keycode_flag")
 
     // 2000 - device controls
-    @Keep @JvmField val USE_APP_PANELS = releasedFlag(2000, "use_app_panels", teamfood = true)
+    @Keep @JvmField val USE_APP_PANELS = releasedFlag(2000, "use_app_panels")
 
     @JvmField
     val APP_PANELS_ALL_APPS_ALLOWED =
-        releasedFlag(2001, "app_panels_all_apps_allowed", teamfood = true)
+        releasedFlag(2001, "app_panels_all_apps_allowed")
 
     @JvmField
     val CONTROLS_MANAGEMENT_NEW_FLOWS =
-        releasedFlag(2002, "controls_management_new_flows", teamfood = true)
+        releasedFlag(2002, "controls_management_new_flows")
 
     // Enables removing app from Home control panel as a part of a new flow
     // TODO(b/269132640): Tracking Bug
diff --git a/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt b/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt
new file mode 100644
index 0000000..801b165
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt
@@ -0,0 +1,493 @@
+package com.android.systemui.graphics
+
+import android.annotation.AnyThread
+import android.annotation.DrawableRes
+import android.annotation.Px
+import android.annotation.SuppressLint
+import android.annotation.WorkerThread
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.res.Resources
+import android.content.res.Resources.NotFoundException
+import android.graphics.Bitmap
+import android.graphics.ImageDecoder
+import android.graphics.ImageDecoder.DecodeException
+import android.graphics.drawable.AdaptiveIconDrawable
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
+import android.util.Log
+import android.util.Size
+import androidx.core.content.res.ResourcesCompat
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import java.io.IOException
+import javax.inject.Inject
+import kotlin.math.min
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.withContext
+
+/**
+ * Helper class to load images for SystemUI. It allows for memory efficient image loading with size
+ * restriction and attempts to use hardware bitmaps when sensible.
+ */
+@SysUISingleton
+class ImageLoader
+@Inject
+constructor(
+    private val defaultContext: Context,
+    @Background private val backgroundDispatcher: CoroutineDispatcher
+) {
+
+    /** Source of the image data. */
+    sealed interface Source
+
+    /**
+     * Load image from a Resource ID. If the resource is part of another package or if it requires
+     * tinting, pass in a correct [Context].
+     */
+    data class Res(@DrawableRes val resId: Int, val context: Context?) : Source {
+        constructor(@DrawableRes resId: Int) : this(resId, null)
+    }
+
+    /** Load image from a Uri. */
+    data class Uri(val uri: android.net.Uri) : Source {
+        constructor(uri: String) : this(android.net.Uri.parse(uri))
+    }
+
+    /** Load image from a [File]. */
+    data class File(val file: java.io.File) : Source {
+        constructor(path: String) : this(java.io.File(path))
+    }
+
+    /** Load image from an [InputStream]. */
+    data class InputStream(val inputStream: java.io.InputStream, val context: Context?) : Source {
+        constructor(inputStream: java.io.InputStream) : this(inputStream, null)
+    }
+
+    /**
+     * Loads passed [Source] on a background thread and returns the [Bitmap].
+     *
+     * Maximum height and width can be passed as optional parameters - the image decoder will make
+     * sure to keep the decoded drawable size within those passed constraints while keeping aspect
+     * ratio.
+     *
+     * @param maxWidth Maximum width of the returned drawable (if able). 0 means no restriction. Set
+     *   to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param maxHeight Maximum height of the returned drawable (if able). 0 means no restriction.
+     *   Set to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param allocator Allocator to use for the loaded drawable - one of [ImageDecoder] allocator
+     *   ints. Use [ImageDecoder.ALLOCATOR_SOFTWARE] to force software bitmap.
+     * @return loaded [Bitmap] or `null` if loading failed.
+     */
+    @AnyThread
+    suspend fun loadBitmap(
+        source: Source,
+        @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+    ): Bitmap? =
+        withContext(backgroundDispatcher) { loadBitmapSync(source, maxWidth, maxHeight, allocator) }
+
+    /**
+     * Loads passed [Source] synchronously and returns the [Bitmap].
+     *
+     * Maximum height and width can be passed as optional parameters - the image decoder will make
+     * sure to keep the decoded drawable size within those passed constraints while keeping aspect
+     * ratio.
+     *
+     * @param maxWidth Maximum width of the returned drawable (if able). 0 means no restriction. Set
+     *   to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param maxHeight Maximum height of the returned drawable (if able). 0 means no restriction.
+     *   Set to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param allocator Allocator to use for the loaded drawable - one of [ImageDecoder] allocator
+     *   ints. Use [ImageDecoder.ALLOCATOR_SOFTWARE] to force software bitmap.
+     * @return loaded [Bitmap] or `null` if loading failed.
+     */
+    @WorkerThread
+    fun loadBitmapSync(
+        source: Source,
+        @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+    ): Bitmap? {
+        return try {
+            loadBitmapSync(
+                toImageDecoderSource(source, defaultContext),
+                maxWidth,
+                maxHeight,
+                allocator
+            )
+        } catch (e: NotFoundException) {
+            Log.w(TAG, "Couldn't load resource $source", e)
+            null
+        }
+    }
+
+    /**
+     * Loads passed [ImageDecoder.Source] synchronously and returns the drawable.
+     *
+     * Maximum height and width can be passed as optional parameters - the image decoder will make
+     * sure to keep the decoded drawable size within those passed constraints (while keeping aspect
+     * ratio).
+     *
+     * @param maxWidth Maximum width of the returned drawable (if able). 0 means no restriction. Set
+     *   to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param maxHeight Maximum height of the returned drawable (if able). 0 means no restriction.
+     *   Set to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param allocator Allocator to use for the loaded drawable - one of [ImageDecoder] allocator
+     *   ints. Use [ImageDecoder.ALLOCATOR_SOFTWARE] to force software bitmap.
+     * @return loaded [Bitmap] or `null` if loading failed.
+     */
+    @WorkerThread
+    fun loadBitmapSync(
+        source: ImageDecoder.Source,
+        @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+    ): Bitmap? {
+        return try {
+            ImageDecoder.decodeBitmap(source) { decoder, info, _ ->
+                configureDecoderForMaximumSize(decoder, info.size, maxWidth, maxHeight)
+                decoder.allocator = allocator
+            }
+        } catch (e: IOException) {
+            Log.w(TAG, "Failed to load source $source", e)
+            return null
+        } catch (e: DecodeException) {
+            Log.w(TAG, "Failed to decode source $source", e)
+            return null
+        }
+    }
+
+    /**
+     * Loads passed [Source] on a background thread and returns the [Drawable].
+     *
+     * Maximum height and width can be passed as optional parameters - the image decoder will make
+     * sure to keep the decoded drawable size within those passed constraints (while keeping aspect
+     * ratio).
+     *
+     * @param maxWidth Maximum width of the returned drawable (if able). 0 means no restriction. Set
+     *   to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param maxHeight Maximum height of the returned drawable (if able). 0 means no restriction.
+     *   Set to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param allocator Allocator to use for the loaded drawable - one of [ImageDecoder] allocator
+     *   ints. Use [ImageDecoder.ALLOCATOR_SOFTWARE] to force software bitmap.
+     * @return loaded [Drawable] or `null` if loading failed.
+     */
+    @AnyThread
+    suspend fun loadDrawable(
+        source: Source,
+        @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+    ): Drawable? =
+        withContext(backgroundDispatcher) {
+            loadDrawableSync(source, maxWidth, maxHeight, allocator)
+        }
+
+    /**
+     * Loads passed [Icon] on a background thread and returns the drawable.
+     *
+     * Maximum height and width can be passed as optional parameters - the image decoder will make
+     * sure to keep the decoded drawable size within those passed constraints (while keeping aspect
+     * ratio).
+     *
+     * @param context Alternate context to use for resource loading (for e.g. cross-process use)
+     * @param maxWidth Maximum width of the returned drawable (if able). 0 means no restriction. Set
+     *   to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param maxHeight Maximum height of the returned drawable (if able). 0 means no restriction.
+     *   Set to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param allocator Allocator to use for the loaded drawable - one of [ImageDecoder] allocator
+     *   ints. Use [ImageDecoder.ALLOCATOR_SOFTWARE] to force software bitmap.
+     * @return loaded [Drawable] or `null` if loading failed.
+     */
+    @AnyThread
+    suspend fun loadDrawable(
+        icon: Icon,
+        context: Context = defaultContext,
+        @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+    ): Drawable? =
+        withContext(backgroundDispatcher) {
+            loadDrawableSync(icon, context, maxWidth, maxHeight, allocator)
+        }
+
+    /**
+     * Loads passed [Source] synchronously and returns the drawable.
+     *
+     * Maximum height and width can be passed as optional parameters - the image decoder will make
+     * sure to keep the decoded drawable size within those passed constraints (while keeping aspect
+     * ratio).
+     *
+     * @param maxWidth Maximum width of the returned drawable (if able). 0 means no restriction. Set
+     *   to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param maxHeight Maximum height of the returned drawable (if able). 0 means no restriction.
+     *   Set to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param allocator Allocator to use for the loaded drawable - one of [ImageDecoder] allocator
+     *   ints. Use [ImageDecoder.ALLOCATOR_SOFTWARE] to force software bitmap.
+     * @return loaded [Drawable] or `null` if loading failed.
+     */
+    @WorkerThread
+    @SuppressLint("UseCompatLoadingForDrawables")
+    fun loadDrawableSync(
+        source: Source,
+        @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+    ): Drawable? {
+        return try {
+            loadDrawableSync(
+                toImageDecoderSource(source, defaultContext),
+                maxWidth,
+                maxHeight,
+                allocator
+            )
+                ?:
+                // If we have a resource, retry fallback using the "normal" Resource loading system.
+                // This will come into effect in cases like trying to load AnimatedVectorDrawable.
+                if (source is Res) {
+                    val context = source.context ?: defaultContext
+                    ResourcesCompat.getDrawable(context.resources, source.resId, context.theme)
+                } else {
+                    null
+                }
+        } catch (e: NotFoundException) {
+            Log.w(TAG, "Couldn't load resource $source", e)
+            null
+        }
+    }
+
+    /**
+     * Loads passed [ImageDecoder.Source] synchronously and returns the drawable.
+     *
+     * Maximum height and width can be passed as optional parameters - the image decoder will make
+     * sure to keep the decoded drawable size within those passed constraints (while keeping aspect
+     * ratio).
+     *
+     * @param maxWidth Maximum width of the returned drawable (if able). 0 means no restriction. Set
+     *   to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param maxHeight Maximum height of the returned drawable (if able). 0 means no restriction.
+     *   Set to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+     * @param allocator Allocator to use for the loaded drawable - one of [ImageDecoder] allocator
+     *   ints. Use [ImageDecoder.ALLOCATOR_SOFTWARE] to force software bitmap.
+     * @return loaded [Drawable] or `null` if loading failed.
+     */
+    @WorkerThread
+    fun loadDrawableSync(
+        source: ImageDecoder.Source,
+        @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+    ): Drawable? {
+        return try {
+            ImageDecoder.decodeDrawable(source) { decoder, info, _ ->
+                configureDecoderForMaximumSize(decoder, info.size, maxWidth, maxHeight)
+                decoder.allocator = allocator
+            }
+        } catch (e: IOException) {
+            Log.w(TAG, "Failed to load source $source", e)
+            return null
+        } catch (e: DecodeException) {
+            Log.w(TAG, "Failed to decode source $source", e)
+            return null
+        }
+    }
+
+    /** Loads icon drawable while attempting to size restrict the drawable. */
+    @WorkerThread
+    fun loadDrawableSync(
+        icon: Icon,
+        context: Context = defaultContext,
+        @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+        allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+    ): Drawable? {
+        return when (icon.type) {
+            Icon.TYPE_URI,
+            Icon.TYPE_URI_ADAPTIVE_BITMAP -> {
+                val source = ImageDecoder.createSource(context.contentResolver, icon.uri)
+                loadDrawableSync(source, maxWidth, maxHeight, allocator)
+            }
+            Icon.TYPE_RESOURCE -> {
+                val resources = resolveResourcesForIcon(context, icon)
+                resources?.let {
+                    loadDrawableSync(
+                        ImageDecoder.createSource(it, icon.resId),
+                        maxWidth,
+                        maxHeight,
+                        allocator
+                    )
+                }
+                // Fallback to non-ImageDecoder load if the attempt failed (e.g. the resource
+                // is a Vector drawable which ImageDecoder doesn't support.)
+                ?: icon.loadDrawable(context)
+            }
+            Icon.TYPE_BITMAP -> {
+                BitmapDrawable(context.resources, icon.bitmap)
+            }
+            Icon.TYPE_ADAPTIVE_BITMAP -> {
+                AdaptiveIconDrawable(null, BitmapDrawable(context.resources, icon.bitmap))
+            }
+            Icon.TYPE_DATA -> {
+                loadDrawableSync(
+                    ImageDecoder.createSource(icon.dataBytes, icon.dataOffset, icon.dataLength),
+                    maxWidth,
+                    maxHeight,
+                    allocator
+                )
+            }
+            else -> {
+                // We don't recognize this icon, just fallback.
+                icon.loadDrawable(context)
+            }
+        }?.let { drawable ->
+            // Icons carry tint which we need to propagate down to a Drawable.
+            tintDrawable(icon, drawable)
+            drawable
+        }
+    }
+
+    companion object {
+        const val TAG = "ImageLoader"
+
+        // 4096 is a reasonable default - most devices will support 4096x4096 texture size for
+        // Canvas rendering and by default we SystemUI has no need to render larger bitmaps.
+        // This prevents exceptions and crashes if the code accidentally loads larger Bitmap
+        // and then attempts to render it on Canvas.
+        // It can always be overridden by the parameters.
+        const val DEFAULT_MAX_SAFE_BITMAP_SIZE_PX = 4096
+
+        /**
+         * This constant signals that ImageLoader shouldn't attempt to resize the passed bitmap in a
+         * given dimension.
+         *
+         * Set both maxWidth and maxHeight to [DO_NOT_RESIZE] if you wish to prevent resizing.
+         */
+        const val DO_NOT_RESIZE = 0
+
+        /** Maps [Source] to [ImageDecoder.Source]. */
+        private fun toImageDecoderSource(source: Source, defaultContext: Context) =
+            when (source) {
+                is Res -> {
+                    val context = source.context ?: defaultContext
+                    ImageDecoder.createSource(context.resources, source.resId)
+                }
+                is File -> ImageDecoder.createSource(source.file)
+                is Uri -> ImageDecoder.createSource(defaultContext.contentResolver, source.uri)
+                is InputStream -> {
+                    val context = source.context ?: defaultContext
+                    ImageDecoder.createSource(context.resources, source.inputStream)
+                }
+            }
+
+        /**
+         * This sets target size on the image decoder to conform to the maxWidth / maxHeight
+         * parameters. The parameters are chosen to keep the existing drawable aspect ratio.
+         */
+        @AnyThread
+        private fun configureDecoderForMaximumSize(
+            decoder: ImageDecoder,
+            imgSize: Size,
+            @Px maxWidth: Int,
+            @Px maxHeight: Int
+        ) {
+            if (maxWidth == DO_NOT_RESIZE && maxHeight == DO_NOT_RESIZE) {
+                return
+            }
+
+            if (imgSize.width <= maxWidth && imgSize.height <= maxHeight) {
+                return
+            }
+
+            // Determine the scale factor for each dimension so it fits within the set constraint
+            val wScale =
+                if (maxWidth <= 0) {
+                    1.0f
+                } else {
+                    maxWidth.toFloat() / imgSize.width.toFloat()
+                }
+
+            val hScale =
+                if (maxHeight <= 0) {
+                    1.0f
+                } else {
+                    maxHeight.toFloat() / imgSize.height.toFloat()
+                }
+
+            // Scale down to the dimension that demands larger scaling (smaller scale factor).
+            // Use the same scale for both dimensions to keep the aspect ratio.
+            val scale = min(wScale, hScale)
+            if (scale < 1.0f) {
+                val targetWidth = (imgSize.width * scale).toInt()
+                val targetHeight = (imgSize.height * scale).toInt()
+                if (Log.isLoggable(TAG, Log.DEBUG)) {
+                    Log.d(TAG, "Configured image size to $targetWidth x $targetHeight")
+                }
+
+                decoder.setTargetSize(targetWidth, targetHeight)
+            }
+        }
+
+        /**
+         * Attempts to retrieve [Resources] class required to load the passed icon. Icons can
+         * originate from other processes so we need to make sure we load them from the right
+         * package source.
+         *
+         * @return [Resources] to load the icon drawble or null if icon doesn't carry a resource or
+         *   the resource package couldn't be resolved.
+         */
+        @WorkerThread
+        private fun resolveResourcesForIcon(context: Context, icon: Icon): Resources? {
+            if (icon.type != Icon.TYPE_RESOURCE) {
+                return null
+            }
+
+            val resources = icon.resources
+            if (resources != null) {
+                return resources
+            }
+
+            val resPackage = icon.resPackage
+            if (
+                resPackage == null || resPackage.isEmpty() || context.packageName.equals(resPackage)
+            ) {
+                return context.resources
+            }
+
+            if ("android" == resPackage) {
+                return Resources.getSystem()
+            }
+
+            val pm = context.packageManager
+            try {
+                val ai =
+                    pm.getApplicationInfo(
+                        resPackage,
+                        PackageManager.MATCH_UNINSTALLED_PACKAGES or
+                            PackageManager.GET_SHARED_LIBRARY_FILES
+                    )
+                if (ai != null) {
+                    return pm.getResourcesForApplication(ai)
+                } else {
+                    Log.w(TAG, "Failed to resolve application info for $resPackage")
+                }
+            } catch (e: PackageManager.NameNotFoundException) {
+                Log.w(TAG, "Failed to resolve resource package", e)
+                return null
+            }
+            return null
+        }
+
+        /** Applies tinting from [Icon] to the passed [Drawable]. */
+        @AnyThread
+        private fun tintDrawable(icon: Icon, drawable: Drawable) {
+            if (icon.hasTint()) {
+                drawable.mutate()
+                drawable.setTintList(icon.tintList)
+                drawable.setTintBlendMode(icon.tintBlendMode)
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index eef7ccc..107e685 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -231,22 +231,20 @@
                 );
             }
 
-            public void mergeAnimation(IBinder transition, TransitionInfo info,
-                    SurfaceControl.Transaction t, IBinder mergeTarget,
-                    IRemoteTransitionFinishedCallback finishCallback) {
+            public void mergeAnimation(IBinder candidateTransition, TransitionInfo candidateInfo,
+                    SurfaceControl.Transaction candidateT, IBinder currentTransition,
+                    IRemoteTransitionFinishedCallback candidateFinishCallback) {
                 try {
-                    final IRemoteTransitionFinishedCallback origFinishCB;
+                    final IRemoteTransitionFinishedCallback currentFinishCB;
                     synchronized (mFinishCallbacks) {
-                        origFinishCB = mFinishCallbacks.remove(transition);
+                        currentFinishCB = mFinishCallbacks.remove(currentTransition);
                     }
-                    info.releaseAllSurfaces();
-                    t.close();
-                    if (origFinishCB == null) {
-                        // already finished (or not started yet), so do nothing.
+                    if (currentFinishCB == null) {
+                        Slog.e(TAG, "Called mergeAnimation, but finish callback is missing");
                         return;
                     }
                     runner.onAnimationCancelled(false /* isKeyguardOccluded */);
-                    origFinishCB.onTransitionFinished(null /* wct */, null /* t */);
+                    currentFinishCB.onTransitionFinished(null /* wct */, null /* t */);
                 } catch (RemoteException e) {
                     // nothing, we'll just let it finish on its own I guess.
                 }
@@ -304,13 +302,13 @@
         Slog.d(TAG, "KeyguardService registerRemote: TRANSIT_KEYGUARD_GOING_AWAY");
         TransitionFilter f = new TransitionFilter();
         f.mFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
-        mShellTransitions.registerRemote(f,
-                new RemoteTransition(wrap(mExitAnimationRunner), getIApplicationThread()));
+        mShellTransitions.registerRemote(f, new RemoteTransition(
+                wrap(mExitAnimationRunner), getIApplicationThread(), "ExitKeyguard"));
 
         Slog.d(TAG, "KeyguardService registerRemote: TRANSIT_KEYGUARD_(UN)OCCLUDE");
         // Register for occluding
         final RemoteTransition occludeTransition = new RemoteTransition(
-                mOccludeAnimation, getIApplicationThread());
+                mOccludeAnimation, getIApplicationThread(), "KeyguardOcclude");
         f = new TransitionFilter();
         f.mFlags = TRANSIT_FLAG_KEYGUARD_LOCKED;
         f.mRequirements = new TransitionFilter.Requirement[]{
@@ -329,7 +327,7 @@
 
         // Now register for un-occlude.
         final RemoteTransition unoccludeTransition = new RemoteTransition(
-                mUnoccludeAnimation, getIApplicationThread());
+                mUnoccludeAnimation, getIApplicationThread(), "KeyguardUnocclude");
         f = new TransitionFilter();
         f.mFlags = TRANSIT_FLAG_KEYGUARD_LOCKED;
         f.mRequirements = new TransitionFilter.Requirement[]{
@@ -384,7 +382,7 @@
         f.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
         mShellTransitions.registerRemote(f, new RemoteTransition(
                 wrap(mKeyguardViewMediator.getOccludeByDreamAnimationRunner()),
-                getIApplicationThread()));
+                getIApplicationThread(), "KeyguardOccludeByDream"));
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 0825435..b1efdd7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -953,10 +953,15 @@
 
                 @Override
                 public void onAnimationCancelled(boolean isKeyguardOccluded) {
-                    if (mOccludeByDreamAnimator != null) {
-                        mOccludeByDreamAnimator.cancel();
-                    }
-                    setOccluded(isKeyguardOccluded /* isOccluded */, false /* animate */);
+                    mContext.getMainExecutor().execute(() -> {
+                        if (mOccludeByDreamAnimator != null) {
+                            mOccludeByDreamAnimator.cancel();
+                        }
+                    });
+                    // The value of isKeyguardOccluded here may come from mergeAnimation, which
+                    // isn't reliable. In all cases, after running or cancelling this animation,
+                    // keyguard should be occluded.
+                    setOccluded(true /* isOccluded */, false /* animate */);
                     if (DEBUG) {
                         Log.d(TAG, "Occlude by Dream animation cancelled. Occluded state is now: "
                                 + mOccluded);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index cb89106..6ac51cd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -43,6 +43,7 @@
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.keyguard.data.quickaffordance.KeyguardDataQuickAffordanceModule;
+import com.android.systemui.keyguard.data.repository.KeyguardFaceAuthModule;
 import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule;
 import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule;
 import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceModule;
@@ -66,8 +67,6 @@
 import dagger.Lazy;
 import dagger.Module;
 import dagger.Provides;
-import kotlinx.coroutines.CoroutineDispatcher;
-import kotlinx.coroutines.CoroutineScope;
 
 /**
  * Dagger Module providing keyguard.
@@ -82,6 +81,7 @@
             KeyguardDataQuickAffordanceModule.class,
             KeyguardQuickAffordanceModule.class,
             KeyguardRepositoryModule.class,
+            KeyguardFaceAuthModule.class,
             StartKeyguardTransitionModule.class,
         })
 public class KeyguardModule {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
index d5129a6..09002fd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt
@@ -87,6 +87,13 @@
      */
     val isStrongBiometricAllowed: StateFlow<Boolean>
 
+    /**
+     * Whether the current user is allowed to use a convenience biometric for device entry based on
+     * Android Security policies. If false, the user may be able to use strong biometric or primary
+     * authentication for device entry.
+     */
+    val isNonStrongBiometricAllowed: StateFlow<Boolean>
+
     /** Whether fingerprint feature is enabled for the current user by the DevicePolicy */
     val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean>
 
@@ -276,6 +283,16 @@
             )
         )
 
+    override val isNonStrongBiometricAllowed: StateFlow<Boolean> =
+        strongAuthTracker.isNonStrongBiometricAllowed.stateIn(
+            scope,
+            SharingStarted.Eagerly,
+            strongAuthTracker.isBiometricAllowedForUser(
+                false,
+                userRepository.getSelectedUserInfo().id
+            )
+        )
+
     override val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean> =
         selectedUserId
             .flatMapLatest { userId ->
@@ -297,40 +314,62 @@
 private class StrongAuthTracker(private val userRepository: UserRepository, context: Context?) :
     LockPatternUtils.StrongAuthTracker(context) {
 
-    private val _authFlags =
+    // Backing field for onStrongAuthRequiredChanged
+    private val _strongAuthFlags =
         MutableStateFlow(
             StrongAuthenticationFlags(currentUserId, getStrongAuthForUser(currentUserId))
         )
 
+    // Backing field for onIsNonStrongBiometricAllowedChanged
+    private val _nonStrongBiometricAllowed =
+        MutableStateFlow(
+            Pair(currentUserId, isNonStrongBiometricAllowedAfterIdleTimeout(currentUserId))
+        )
+
     val currentUserAuthFlags: Flow<StrongAuthenticationFlags> =
         userRepository.selectedUserInfo
             .map { it.id }
             .distinctUntilChanged()
-            .flatMapLatest { currUserId ->
-                _authFlags
-                    .filter { it.userId == currUserId }
+            .flatMapLatest { userId ->
+                _strongAuthFlags
+                    .filter { it.userId == userId }
                     .onEach { Log.d(TAG, "currentUser authFlags changed, new value: $it") }
                     .onStart {
-                        emit(
-                            StrongAuthenticationFlags(
-                                currentUserId,
-                                getStrongAuthForUser(currentUserId)
-                            )
-                        )
+                        emit(StrongAuthenticationFlags(userId, getStrongAuthForUser(userId)))
                     }
             }
 
+    /** isStrongBiometricAllowed for the current user. */
     val isStrongBiometricAllowed: Flow<Boolean> =
         currentUserAuthFlags.map { isBiometricAllowedForUser(true, it.userId) }
 
+    /** isNonStrongBiometricAllowed for the current user. */
+    val isNonStrongBiometricAllowed: Flow<Boolean> =
+        userRepository.selectedUserInfo
+            .map { it.id }
+            .distinctUntilChanged()
+            .flatMapLatest { userId ->
+                _nonStrongBiometricAllowed
+                    .filter { it.first == userId }
+                    .map { it.second }
+                    .onEach { Log.d(TAG, "isNonStrongBiometricAllowed changed for current user") }
+                    .onStart { emit(isNonStrongBiometricAllowedAfterIdleTimeout(userId)) }
+            }
+
     private val currentUserId
         get() = userRepository.getSelectedUserInfo().id
 
     override fun onStrongAuthRequiredChanged(userId: Int) {
         val newFlags = getStrongAuthForUser(userId)
-        _authFlags.value = StrongAuthenticationFlags(userId, newFlags)
+        _strongAuthFlags.value = StrongAuthenticationFlags(userId, newFlags)
         Log.d(TAG, "onStrongAuthRequiredChanged for userId: $userId, flag value: $newFlags")
     }
+
+    override fun onIsNonStrongBiometricAllowedChanged(userId: Int) {
+        val allowed = isNonStrongBiometricAllowedAfterIdleTimeout(userId)
+        _nonStrongBiometricAllowed.value = Pair(userId, allowed)
+        Log.d(TAG, "onIsNonStrongBiometricAllowedChanged for userId: $userId, $allowed")
+    }
 }
 
 private fun DevicePolicyManager.isFaceDisabled(userId: Int): Boolean =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
new file mode 100644
index 0000000..56e7398
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -0,0 +1,540 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.app.StatusBarManager
+import android.content.Context
+import android.hardware.face.FaceAuthenticateOptions
+import android.hardware.face.FaceManager
+import android.os.CancellationSignal
+import com.android.internal.logging.InstanceId
+import com.android.internal.logging.UiEventLogger
+import com.android.keyguard.FaceAuthUiEvent
+import com.android.systemui.Dumpable
+import com.android.systemui.R
+import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
+import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.shared.model.AcquiredAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.AuthenticationStatus
+import com.android.systemui.keyguard.shared.model.DetectionStatus
+import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FailedAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.log.FaceAuthenticationLogger
+import com.android.systemui.log.SessionTracker
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.user.data.repository.UserRepository
+import java.io.PrintWriter
+import java.util.Arrays
+import java.util.stream.Collectors
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * API to run face authentication and detection for device entry / on keyguard (as opposed to the
+ * biometric prompt).
+ */
+interface DeviceEntryFaceAuthRepository {
+    /** Provide the current face authentication state for device entry. */
+    val isAuthenticated: Flow<Boolean>
+
+    /** Whether face auth can run at this point. */
+    val canRunFaceAuth: Flow<Boolean>
+
+    /** Provide the current status of face authentication. */
+    val authenticationStatus: Flow<AuthenticationStatus>
+
+    /** Provide the current status of face detection. */
+    val detectionStatus: Flow<DetectionStatus>
+
+    /** Current state of whether face authentication is locked out or not. */
+    val isLockedOut: Flow<Boolean>
+
+    /** Current state of whether face authentication is running. */
+    val isAuthRunning: Flow<Boolean>
+
+    /**
+     * Trigger face authentication.
+     *
+     * [uiEvent] provided should be logged whenever face authentication runs. Invocation should be
+     * ignored if face authentication is already running. Results should be propagated through
+     * [authenticationStatus]
+     *
+     * Run only face detection when [fallbackToDetection] is true and [canRunFaceAuth] is false.
+     */
+    suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean = false)
+
+    /** Stop currently running face authentication or detection. */
+    fun cancel()
+}
+
+@SysUISingleton
+class DeviceEntryFaceAuthRepositoryImpl
+@Inject
+constructor(
+    context: Context,
+    private val faceManager: FaceManager? = null,
+    private val userRepository: UserRepository,
+    private val keyguardBypassController: KeyguardBypassController? = null,
+    @Application private val applicationScope: CoroutineScope,
+    @Main private val mainDispatcher: CoroutineDispatcher,
+    private val sessionTracker: SessionTracker,
+    private val uiEventsLogger: UiEventLogger,
+    private val faceAuthLogger: FaceAuthenticationLogger,
+    private val biometricSettingsRepository: BiometricSettingsRepository,
+    private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
+    private val trustRepository: TrustRepository,
+    private val keyguardRepository: KeyguardRepository,
+    private val keyguardInteractor: KeyguardInteractor,
+    private val alternateBouncerInteractor: AlternateBouncerInteractor,
+    dumpManager: DumpManager,
+) : DeviceEntryFaceAuthRepository, Dumpable {
+    private var authCancellationSignal: CancellationSignal? = null
+    private var detectCancellationSignal: CancellationSignal? = null
+    private var faceAcquiredInfoIgnoreList: Set<Int>
+
+    private var cancelNotReceivedHandlerJob: Job? = null
+
+    private val _authenticationStatus: MutableStateFlow<AuthenticationStatus?> =
+        MutableStateFlow(null)
+    override val authenticationStatus: Flow<AuthenticationStatus>
+        get() = _authenticationStatus.filterNotNull()
+
+    private val _detectionStatus = MutableStateFlow<DetectionStatus?>(null)
+    override val detectionStatus: Flow<DetectionStatus>
+        get() = _detectionStatus.filterNotNull()
+
+    private val _isLockedOut = MutableStateFlow(false)
+    override val isLockedOut: StateFlow<Boolean> = _isLockedOut
+
+    val isDetectionSupported =
+        faceManager?.sensorPropertiesInternal?.firstOrNull()?.supportsFaceDetection ?: false
+
+    private val _isAuthRunning = MutableStateFlow(false)
+    override val isAuthRunning: StateFlow<Boolean>
+        get() = _isAuthRunning
+
+    private val keyguardSessionId: InstanceId?
+        get() = sessionTracker.getSessionId(StatusBarManager.SESSION_KEYGUARD)
+
+    private val _canRunFaceAuth = MutableStateFlow(true)
+    override val canRunFaceAuth: StateFlow<Boolean>
+        get() = _canRunFaceAuth
+
+    private val canRunDetection = MutableStateFlow(false)
+
+    private val _isAuthenticated = MutableStateFlow(false)
+    override val isAuthenticated: Flow<Boolean>
+        get() = _isAuthenticated
+
+    private val bypassEnabled: Flow<Boolean> =
+        keyguardBypassController?.let {
+            conflatedCallbackFlow {
+                val callback =
+                    object : KeyguardBypassController.OnBypassStateChangedListener {
+                        override fun onBypassStateChanged(isEnabled: Boolean) {
+                            trySendWithFailureLogging(isEnabled, TAG, "BypassStateChanged")
+                        }
+                    }
+                it.registerOnBypassStateChangedListener(callback)
+                trySendWithFailureLogging(it.bypassEnabled, TAG, "BypassStateChanged")
+                awaitClose { it.unregisterOnBypassStateChangedListener(callback) }
+            }
+        }
+            ?: flowOf(false)
+
+    private val faceLockoutResetCallback =
+        object : FaceManager.LockoutResetCallback() {
+            override fun onLockoutReset(sensorId: Int) {
+                _isLockedOut.value = false
+            }
+        }
+
+    init {
+        faceManager?.addLockoutResetCallback(faceLockoutResetCallback)
+        faceAcquiredInfoIgnoreList =
+            Arrays.stream(
+                    context.resources.getIntArray(
+                        R.array.config_face_acquire_device_entry_ignorelist
+                    )
+                )
+                .boxed()
+                .collect(Collectors.toSet())
+        dumpManager.registerCriticalDumpable("DeviceEntryFaceAuthRepositoryImpl", this)
+
+        observeFaceAuthGatingChecks()
+        observeFaceDetectGatingChecks()
+        observeFaceAuthResettingConditions()
+    }
+
+    private fun observeFaceAuthResettingConditions() {
+        // Clear auth status when keyguard is going away or when the user is switching.
+        merge(keyguardRepository.isKeyguardGoingAway, userRepository.userSwitchingInProgress)
+            .onEach { goingAwayOrUserSwitchingInProgress ->
+                if (goingAwayOrUserSwitchingInProgress) {
+                    _isAuthenticated.value = false
+                }
+            }
+            .launchIn(applicationScope)
+    }
+
+    private fun observeFaceDetectGatingChecks() {
+        // Face detection can run only when lockscreen bypass is enabled
+        // & detection is supported & biometric unlock is not allowed.
+        listOf(
+                canFaceAuthOrDetectRun(),
+                logAndObserve(bypassEnabled, "bypassEnabled"),
+                logAndObserve(
+                    biometricSettingsRepository.isNonStrongBiometricAllowed.isFalse(),
+                    "nonStrongBiometricIsNotAllowed"
+                ),
+                // We don't want to run face detect if it's not possible to authenticate with FP
+                // from the bouncer. UDFPS is the only fp sensor type that won't support this.
+                logAndObserve(
+                    and(isUdfps(), deviceEntryFingerprintAuthRepository.isRunning).isFalse(),
+                    "udfpsAuthIsNotPossibleAnymore"
+                )
+            )
+            .reduce(::and)
+            .distinctUntilChanged()
+            .onEach {
+                faceAuthLogger.canRunDetectionChanged(it)
+                canRunDetection.value = it
+                if (!it) {
+                    cancelDetection()
+                }
+            }
+            .launchIn(applicationScope)
+    }
+
+    private fun isUdfps() =
+        deviceEntryFingerprintAuthRepository.availableFpSensorType.map {
+            it == BiometricType.UNDER_DISPLAY_FINGERPRINT
+        }
+
+    private fun canFaceAuthOrDetectRun(): Flow<Boolean> {
+        return listOf(
+                logAndObserve(biometricSettingsRepository.isFaceEnrolled, "isFaceEnrolled"),
+                logAndObserve(
+                    biometricSettingsRepository.isFaceAuthenticationEnabled,
+                    "isFaceAuthenticationEnabled"
+                ),
+                logAndObserve(
+                    userRepository.userSwitchingInProgress.isFalse(),
+                    "userSwitchingNotInProgress"
+                ),
+                logAndObserve(
+                    keyguardRepository.isKeyguardGoingAway.isFalse(),
+                    "keyguardNotGoingAway"
+                ),
+                logAndObserve(
+                    keyguardRepository.wakefulness
+                        .map { WakefulnessModel.isSleepingOrStartingToSleep(it) }
+                        .isFalse(),
+                    "deviceNotSleepingOrNotStartingToSleep"
+                ),
+                logAndObserve(
+                    combine(
+                        keyguardInteractor.isSecureCameraActive,
+                        alternateBouncerInteractor.isVisible,
+                    ) { a, b ->
+                        !a || b
+                    },
+                    "secureCameraNotActiveOrAltBouncerIsShowing"
+                ),
+                logAndObserve(
+                    biometricSettingsRepository.isFaceAuthSupportedInCurrentPosture,
+                    "isFaceAuthSupportedInCurrentPosture"
+                ),
+                logAndObserve(
+                    biometricSettingsRepository.isCurrentUserInLockdown.isFalse(),
+                    "userHasNotLockedDownDevice"
+                )
+            )
+            .reduce(::and)
+    }
+
+    private fun observeFaceAuthGatingChecks() {
+        // Face auth can run only if all of the gating conditions are true.
+        listOf(
+                canFaceAuthOrDetectRun(),
+                logAndObserve(isLockedOut.isFalse(), "isNotLocked"),
+                logAndObserve(
+                    deviceEntryFingerprintAuthRepository.isLockedOut.isFalse(),
+                    "fpLockedOut"
+                ),
+                logAndObserve(trustRepository.isCurrentUserTrusted.isFalse(), "currentUserTrusted"),
+                logAndObserve(
+                    biometricSettingsRepository.isNonStrongBiometricAllowed,
+                    "nonStrongBiometricIsAllowed"
+                ),
+                logAndObserve(
+                    userRepository.selectedUserInfo.map { it.isPrimary },
+                    "userIsPrimaryUser"
+                ),
+            )
+            .reduce(::and)
+            .distinctUntilChanged()
+            .onEach {
+                faceAuthLogger.canFaceAuthRunChanged(it)
+                _canRunFaceAuth.value = it
+                if (!it) {
+                    // Cancel currently running auth if any of the gating checks are false.
+                    faceAuthLogger.cancellingFaceAuth()
+                    cancel()
+                }
+            }
+            .launchIn(applicationScope)
+    }
+
+    private val faceAuthCallback =
+        object : FaceManager.AuthenticationCallback() {
+            override fun onAuthenticationFailed() {
+                _authenticationStatus.value = FailedAuthenticationStatus
+                _isAuthenticated.value = false
+                faceAuthLogger.authenticationFailed()
+                onFaceAuthRequestCompleted()
+            }
+
+            override fun onAuthenticationAcquired(acquireInfo: Int) {
+                _authenticationStatus.value = AcquiredAuthenticationStatus(acquireInfo)
+                faceAuthLogger.authenticationAcquired(acquireInfo)
+            }
+
+            override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) {
+                val errorStatus = ErrorAuthenticationStatus(errorCode, errString.toString())
+                if (errorStatus.isLockoutError()) {
+                    _isLockedOut.value = true
+                }
+                _authenticationStatus.value = errorStatus
+                _isAuthenticated.value = false
+                if (errorStatus.isCancellationError()) {
+                    cancelNotReceivedHandlerJob?.cancel()
+                    applicationScope.launch {
+                        faceAuthLogger.launchingQueuedFaceAuthRequest(
+                            faceAuthRequestedWhileCancellation
+                        )
+                        faceAuthRequestedWhileCancellation?.let { authenticate(it) }
+                        faceAuthRequestedWhileCancellation = null
+                    }
+                }
+                faceAuthLogger.authenticationError(
+                    errorCode,
+                    errString,
+                    errorStatus.isLockoutError(),
+                    errorStatus.isCancellationError()
+                )
+                onFaceAuthRequestCompleted()
+            }
+
+            override fun onAuthenticationHelp(code: Int, helpStr: CharSequence?) {
+                if (faceAcquiredInfoIgnoreList.contains(code)) {
+                    return
+                }
+                _authenticationStatus.value = HelpAuthenticationStatus(code, helpStr.toString())
+            }
+
+            override fun onAuthenticationSucceeded(result: FaceManager.AuthenticationResult) {
+                _authenticationStatus.value = SuccessAuthenticationStatus(result)
+                _isAuthenticated.value = true
+                faceAuthLogger.faceAuthSuccess(result)
+                onFaceAuthRequestCompleted()
+            }
+        }
+
+    private fun onFaceAuthRequestCompleted() {
+        cancellationInProgress = false
+        _isAuthRunning.value = false
+        authCancellationSignal = null
+    }
+
+    private val detectionCallback =
+        FaceManager.FaceDetectionCallback { sensorId, userId, isStrong ->
+            faceAuthLogger.faceDetected()
+            _detectionStatus.value = DetectionStatus(sensorId, userId, isStrong)
+        }
+
+    private var cancellationInProgress = false
+    private var faceAuthRequestedWhileCancellation: FaceAuthUiEvent? = null
+
+    override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
+        if (_isAuthRunning.value) {
+            faceAuthLogger.ignoredFaceAuthTrigger(uiEvent)
+            return
+        }
+
+        if (cancellationInProgress) {
+            faceAuthLogger.queuingRequestWhileCancelling(
+                faceAuthRequestedWhileCancellation,
+                uiEvent
+            )
+            faceAuthRequestedWhileCancellation = uiEvent
+            return
+        } else {
+            faceAuthRequestedWhileCancellation = null
+        }
+
+        if (canRunFaceAuth.value) {
+            withContext(mainDispatcher) {
+                // We always want to invoke face auth in the main thread.
+                authCancellationSignal = CancellationSignal()
+                _isAuthRunning.value = true
+                uiEventsLogger.logWithInstanceIdAndPosition(
+                    uiEvent,
+                    0,
+                    null,
+                    keyguardSessionId,
+                    uiEvent.extraInfo
+                )
+                faceAuthLogger.authenticating(uiEvent)
+                faceManager?.authenticate(
+                    null,
+                    authCancellationSignal,
+                    faceAuthCallback,
+                    null,
+                    FaceAuthenticateOptions.Builder().setUserId(currentUserId).build()
+                )
+            }
+        } else if (fallbackToDetection && canRunDetection.value) {
+            detect()
+        }
+    }
+
+    suspend fun detect() {
+        if (!isDetectionSupported) {
+            faceAuthLogger.detectionNotSupported(faceManager, faceManager?.sensorPropertiesInternal)
+            return
+        }
+        if (_isAuthRunning.value || detectCancellationSignal != null) {
+            faceAuthLogger.skippingDetection(_isAuthRunning.value, detectCancellationSignal != null)
+            return
+        }
+
+        detectCancellationSignal = CancellationSignal()
+        withContext(mainDispatcher) {
+            // We always want to invoke face detect in the main thread.
+            faceAuthLogger.faceDetectionStarted()
+            faceManager?.detectFace(
+                detectCancellationSignal,
+                detectionCallback,
+                FaceAuthenticateOptions.Builder().setUserId(currentUserId).build()
+            )
+        }
+    }
+
+    private val currentUserId: Int
+        get() = userRepository.getSelectedUserInfo().id
+
+    fun cancelDetection() {
+        detectCancellationSignal?.cancel()
+        detectCancellationSignal = null
+    }
+
+    override fun cancel() {
+        if (authCancellationSignal == null) return
+
+        authCancellationSignal?.cancel()
+        cancelNotReceivedHandlerJob =
+            applicationScope.launch {
+                delay(DEFAULT_CANCEL_SIGNAL_TIMEOUT)
+                faceAuthLogger.cancelSignalNotReceived(
+                    _isAuthRunning.value,
+                    _isLockedOut.value,
+                    cancellationInProgress,
+                    faceAuthRequestedWhileCancellation
+                )
+                onFaceAuthRequestCompleted()
+            }
+        cancellationInProgress = true
+        _isAuthRunning.value = false
+    }
+
+    private fun logAndObserve(cond: Flow<Boolean>, loggingContext: String): Flow<Boolean> {
+        return cond.distinctUntilChanged().onEach {
+            faceAuthLogger.observedConditionChanged(it, loggingContext)
+        }
+    }
+
+    companion object {
+        const val TAG = "DeviceEntryFaceAuthRepository"
+
+        /**
+         * If no cancel signal has been received after this amount of time, assume that it is
+         * cancelled.
+         */
+        const val DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000L
+    }
+
+    override fun dump(pw: PrintWriter, args: Array<out String>) {
+        pw.println("DeviceEntryFaceAuthRepositoryImpl state:")
+        pw.println("  cancellationInProgress: $cancellationInProgress")
+        pw.println("  _isLockedOut.value: ${_isLockedOut.value}")
+        pw.println("  _isAuthRunning.value: ${_isAuthRunning.value}")
+        pw.println("  isDetectionSupported: $isDetectionSupported")
+        pw.println("  FaceManager state:")
+        pw.println("    faceManager: $faceManager")
+        pw.println("    sensorPropertiesInternal: ${faceManager?.sensorPropertiesInternal}")
+        pw.println(
+            "    supportsFaceDetection: " +
+                "${faceManager?.sensorPropertiesInternal?.firstOrNull()?.supportsFaceDetection}"
+        )
+        pw.println(
+            "  faceAuthRequestedWhileCancellation: ${faceAuthRequestedWhileCancellation?.reason}"
+        )
+        pw.println("  authCancellationSignal: $authCancellationSignal")
+        pw.println("  detectCancellationSignal: $detectCancellationSignal")
+        pw.println("  faceAcquiredInfoIgnoreList: $faceAcquiredInfoIgnoreList")
+        pw.println("  _authenticationStatus: ${_authenticationStatus.value}")
+        pw.println("  _detectionStatus: ${_detectionStatus.value}")
+        pw.println("  currentUserId: $currentUserId")
+        pw.println("  keyguardSessionId: $keyguardSessionId")
+        pw.println("  lockscreenBypassEnabled: ${keyguardBypassController?.bypassEnabled ?: false}")
+    }
+}
+/** Combine two boolean flows by and-ing both of them */
+private fun and(flow: Flow<Boolean>, anotherFlow: Flow<Boolean>) =
+    flow.combine(anotherFlow) { a, b -> a && b }
+
+/** "Not" the given flow. The return [Flow] will be true when [this] flow is false. */
+private fun Flow<Boolean>.isFalse(): Flow<Boolean> {
+    return this.map { !it }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
index 4fa56ee..52234b3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.keyguard.data.repository
 
+import android.hardware.biometrics.BiometricAuthenticator
+import android.hardware.biometrics.BiometricAuthenticator.Modality
 import android.hardware.biometrics.BiometricSourceType
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
@@ -33,6 +35,7 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.stateIn
 
 /** Encapsulates state about device entry fingerprint auth mechanism. */
@@ -49,7 +52,7 @@
     /**
      * Fingerprint sensor type present on the device, null if fingerprint sensor is not available.
      */
-    val availableFpSensorType: BiometricType?
+    val availableFpSensorType: Flow<BiometricType?>
 }
 
 /**
@@ -77,11 +80,39 @@
         pw.println("isLockedOut=${isLockedOut.value}")
     }
 
-    override val availableFpSensorType: BiometricType?
-        get() =
-            if (authController.isUdfpsSupported) BiometricType.UNDER_DISPLAY_FINGERPRINT
-            else if (authController.isSfpsSupported) BiometricType.SIDE_FINGERPRINT
-            else if (authController.isRearFpsSupported) BiometricType.REAR_FINGERPRINT else null
+    override val availableFpSensorType: Flow<BiometricType?>
+        get() {
+            return if (authController.areAllFingerprintAuthenticatorsRegistered()) {
+                flowOf(getFpSensorType())
+            } else {
+                conflatedCallbackFlow {
+                    val callback =
+                        object : AuthController.Callback {
+                            override fun onAllAuthenticatorsRegistered(@Modality modality: Int) {
+                                if (modality == BiometricAuthenticator.TYPE_FINGERPRINT)
+                                    trySendWithFailureLogging(
+                                        getFpSensorType(),
+                                        TAG,
+                                        "onAllAuthenticatorsRegistered, emitting fpSensorType"
+                                    )
+                            }
+                        }
+                    authController.addCallback(callback)
+                    trySendWithFailureLogging(
+                        getFpSensorType(),
+                        TAG,
+                        "initial value for fpSensorType"
+                    )
+                    awaitClose { authController.removeCallback(callback) }
+                }
+            }
+        }
+
+    private fun getFpSensorType(): BiometricType? {
+        return if (authController.isUdfpsSupported) BiometricType.UNDER_DISPLAY_FINGERPRINT
+        else if (authController.isSfpsSupported) BiometricType.SIDE_FINGERPRINT
+        else if (authController.isRearFpsSupported) BiometricType.REAR_FINGERPRINT else null
+    }
 
     override val isLockedOut: StateFlow<Boolean> =
         conflatedCallbackFlow {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
index 64e2a2c..0b506cf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
@@ -65,8 +65,6 @@
     val keyguardAuthenticated: StateFlow<Boolean?>
     val showMessage: StateFlow<BouncerShowMessageModel?>
     val resourceUpdateRequests: StateFlow<Boolean>
-    val bouncerPromptReason: Int
-    val bouncerErrorMessage: CharSequence?
     val alternateBouncerVisible: StateFlow<Boolean>
     val alternateBouncerUIAvailable: StateFlow<Boolean>
     val sideFpsShowing: StateFlow<Boolean>
@@ -145,11 +143,6 @@
     override val showMessage = _showMessage.asStateFlow()
     private val _resourceUpdateRequests = MutableStateFlow(false)
     override val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow()
-    override val bouncerPromptReason: Int
-        get() = viewMediatorCallback.bouncerPromptReason
-    override val bouncerErrorMessage: CharSequence?
-        get() = viewMediatorCallback.consumeCustomMessage()
-
     /** Values associated with the AlternateBouncer */
     private val _alternateBouncerVisible = MutableStateFlow(false)
     override val alternateBouncerVisible = _alternateBouncerVisible.asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManager.kt
deleted file mode 100644
index a326840..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManager.kt
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.data.repository
-
-import android.app.StatusBarManager
-import android.content.Context
-import android.hardware.face.FaceAuthenticateOptions
-import android.hardware.face.FaceManager
-import android.os.CancellationSignal
-import com.android.internal.logging.InstanceId
-import com.android.internal.logging.UiEventLogger
-import com.android.keyguard.FaceAuthUiEvent
-import com.android.systemui.Dumpable
-import com.android.systemui.R
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.keyguard.shared.model.AcquiredAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FailedAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
-import com.android.systemui.log.FaceAuthenticationLogger
-import com.android.systemui.log.SessionTracker
-import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.user.data.repository.UserRepository
-import java.io.PrintWriter
-import java.util.Arrays
-import java.util.stream.Collectors
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-
-/**
- * API to run face authentication and detection for device entry / on keyguard (as opposed to the
- * biometric prompt).
- */
-interface KeyguardFaceAuthManager {
-    /**
-     * Trigger face authentication.
-     *
-     * [uiEvent] provided should be logged whenever face authentication runs. Invocation should be
-     * ignored if face authentication is already running. Results should be propagated through
-     * [authenticationStatus]
-     */
-    suspend fun authenticate(uiEvent: FaceAuthUiEvent)
-
-    /**
-     * Trigger face detection.
-     *
-     * Invocation should be ignored if face authentication is currently running.
-     */
-    suspend fun detect()
-
-    /** Stop currently running face authentication or detection. */
-    fun cancel()
-
-    /** Provide the current status of face authentication. */
-    val authenticationStatus: Flow<AuthenticationStatus>
-
-    /** Provide the current status of face detection. */
-    val detectionStatus: Flow<DetectionStatus>
-
-    /** Current state of whether face authentication is locked out or not. */
-    val isLockedOut: Flow<Boolean>
-
-    /** Current state of whether face authentication is running. */
-    val isAuthRunning: Flow<Boolean>
-
-    /** Is face detection supported. */
-    val isDetectionSupported: Boolean
-}
-
-@SysUISingleton
-class KeyguardFaceAuthManagerImpl
-@Inject
-constructor(
-    context: Context,
-    private val faceManager: FaceManager? = null,
-    private val userRepository: UserRepository,
-    private val keyguardBypassController: KeyguardBypassController? = null,
-    @Application private val applicationScope: CoroutineScope,
-    @Main private val mainDispatcher: CoroutineDispatcher,
-    private val sessionTracker: SessionTracker,
-    private val uiEventsLogger: UiEventLogger,
-    private val faceAuthLogger: FaceAuthenticationLogger,
-    dumpManager: DumpManager,
-) : KeyguardFaceAuthManager, Dumpable {
-    private var cancellationSignal: CancellationSignal? = null
-    private val lockscreenBypassEnabled: Boolean
-        get() = keyguardBypassController?.bypassEnabled ?: false
-    private var faceAcquiredInfoIgnoreList: Set<Int>
-
-    private val faceLockoutResetCallback =
-        object : FaceManager.LockoutResetCallback() {
-            override fun onLockoutReset(sensorId: Int) {
-                _isLockedOut.value = false
-            }
-        }
-
-    init {
-        faceManager?.addLockoutResetCallback(faceLockoutResetCallback)
-        faceAcquiredInfoIgnoreList =
-            Arrays.stream(
-                    context.resources.getIntArray(
-                        R.array.config_face_acquire_device_entry_ignorelist
-                    )
-                )
-                .boxed()
-                .collect(Collectors.toSet())
-        dumpManager.registerCriticalDumpable("KeyguardFaceAuthManagerImpl", this)
-    }
-
-    private val faceAuthCallback =
-        object : FaceManager.AuthenticationCallback() {
-            override fun onAuthenticationFailed() {
-                _authenticationStatus.value = FailedAuthenticationStatus
-                faceAuthLogger.authenticationFailed()
-                onFaceAuthRequestCompleted()
-            }
-
-            override fun onAuthenticationAcquired(acquireInfo: Int) {
-                _authenticationStatus.value = AcquiredAuthenticationStatus(acquireInfo)
-                faceAuthLogger.authenticationAcquired(acquireInfo)
-            }
-
-            override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) {
-                val errorStatus = ErrorAuthenticationStatus(errorCode, errString.toString())
-                if (errorStatus.isLockoutError()) {
-                    _isLockedOut.value = true
-                }
-                _authenticationStatus.value = errorStatus
-                if (errorStatus.isCancellationError()) {
-                    cancelNotReceivedHandlerJob?.cancel()
-                    applicationScope.launch {
-                        faceAuthLogger.launchingQueuedFaceAuthRequest(
-                            faceAuthRequestedWhileCancellation
-                        )
-                        faceAuthRequestedWhileCancellation?.let { authenticate(it) }
-                        faceAuthRequestedWhileCancellation = null
-                    }
-                }
-                faceAuthLogger.authenticationError(
-                    errorCode,
-                    errString,
-                    errorStatus.isLockoutError(),
-                    errorStatus.isCancellationError()
-                )
-                onFaceAuthRequestCompleted()
-            }
-
-            override fun onAuthenticationHelp(code: Int, helpStr: CharSequence?) {
-                if (faceAcquiredInfoIgnoreList.contains(code)) {
-                    return
-                }
-                _authenticationStatus.value = HelpAuthenticationStatus(code, helpStr.toString())
-            }
-
-            override fun onAuthenticationSucceeded(result: FaceManager.AuthenticationResult) {
-                _authenticationStatus.value = SuccessAuthenticationStatus(result)
-                faceAuthLogger.faceAuthSuccess(result)
-                onFaceAuthRequestCompleted()
-            }
-        }
-
-    private fun onFaceAuthRequestCompleted() {
-        cancellationInProgress = false
-        _isAuthRunning.value = false
-        cancellationSignal = null
-    }
-
-    private val detectionCallback =
-        FaceManager.FaceDetectionCallback { sensorId, userId, isStrong ->
-            faceAuthLogger.faceDetected()
-            _detectionStatus.value = DetectionStatus(sensorId, userId, isStrong)
-        }
-
-    private var cancellationInProgress = false
-    private var faceAuthRequestedWhileCancellation: FaceAuthUiEvent? = null
-
-    override suspend fun authenticate(uiEvent: FaceAuthUiEvent) {
-        if (_isAuthRunning.value) {
-            faceAuthLogger.ignoredFaceAuthTrigger(uiEvent)
-            return
-        }
-
-        if (cancellationInProgress) {
-            faceAuthLogger.queuingRequestWhileCancelling(
-                faceAuthRequestedWhileCancellation,
-                uiEvent
-            )
-            faceAuthRequestedWhileCancellation = uiEvent
-            return
-        } else {
-            faceAuthRequestedWhileCancellation = null
-        }
-
-        withContext(mainDispatcher) {
-            // We always want to invoke face auth in the main thread.
-            cancellationSignal = CancellationSignal()
-            _isAuthRunning.value = true
-            uiEventsLogger.logWithInstanceIdAndPosition(
-                uiEvent,
-                0,
-                null,
-                keyguardSessionId,
-                uiEvent.extraInfo
-            )
-            faceAuthLogger.authenticating(uiEvent)
-            faceManager?.authenticate(
-                null,
-                cancellationSignal,
-                faceAuthCallback,
-                null,
-                FaceAuthenticateOptions.Builder().setUserId(currentUserId).build()
-            )
-        }
-    }
-
-    override suspend fun detect() {
-        if (!isDetectionSupported) {
-            faceAuthLogger.detectionNotSupported(faceManager, faceManager?.sensorPropertiesInternal)
-            return
-        }
-        if (_isAuthRunning.value) {
-            faceAuthLogger.skippingBecauseAlreadyRunning("detection")
-            return
-        }
-
-        cancellationSignal = CancellationSignal()
-        withContext(mainDispatcher) {
-            // We always want to invoke face detect in the main thread.
-            faceAuthLogger.faceDetectionStarted()
-            faceManager?.detectFace(
-                cancellationSignal,
-                detectionCallback,
-                FaceAuthenticateOptions.Builder().setUserId(currentUserId).build()
-            )
-        }
-    }
-
-    private val currentUserId: Int
-        get() = userRepository.getSelectedUserInfo().id
-
-    override fun cancel() {
-        if (cancellationSignal == null) return
-
-        cancellationSignal?.cancel()
-        cancelNotReceivedHandlerJob =
-            applicationScope.launch {
-                delay(DEFAULT_CANCEL_SIGNAL_TIMEOUT)
-                faceAuthLogger.cancelSignalNotReceived(
-                    _isAuthRunning.value,
-                    _isLockedOut.value,
-                    cancellationInProgress,
-                    faceAuthRequestedWhileCancellation
-                )
-                onFaceAuthRequestCompleted()
-            }
-        cancellationInProgress = true
-        _isAuthRunning.value = false
-    }
-
-    private var cancelNotReceivedHandlerJob: Job? = null
-
-    private val _authenticationStatus: MutableStateFlow<AuthenticationStatus?> =
-        MutableStateFlow(null)
-    override val authenticationStatus: Flow<AuthenticationStatus>
-        get() = _authenticationStatus.filterNotNull()
-
-    private val _detectionStatus = MutableStateFlow<DetectionStatus?>(null)
-    override val detectionStatus: Flow<DetectionStatus>
-        get() = _detectionStatus.filterNotNull()
-
-    private val _isLockedOut = MutableStateFlow(false)
-    override val isLockedOut: Flow<Boolean> = _isLockedOut
-
-    override val isDetectionSupported =
-        faceManager?.sensorPropertiesInternal?.firstOrNull()?.supportsFaceDetection ?: false
-
-    private val _isAuthRunning = MutableStateFlow(false)
-    override val isAuthRunning: Flow<Boolean>
-        get() = _isAuthRunning
-
-    private val keyguardSessionId: InstanceId?
-        get() = sessionTracker.getSessionId(StatusBarManager.SESSION_KEYGUARD)
-
-    companion object {
-        const val TAG = "KeyguardFaceAuthManager"
-
-        /**
-         * If no cancel signal has been received after this amount of time, assume that it is
-         * cancelled.
-         */
-        const val DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000L
-    }
-
-    override fun dump(pw: PrintWriter, args: Array<out String>) {
-        pw.println("KeyguardFaceAuthManagerImpl state:")
-        pw.println("  cancellationInProgress: $cancellationInProgress")
-        pw.println("  _isLockedOut.value: ${_isLockedOut.value}")
-        pw.println("  _isAuthRunning.value: ${_isAuthRunning.value}")
-        pw.println("  isDetectionSupported: $isDetectionSupported")
-        pw.println("  FaceManager state:")
-        pw.println("    faceManager: $faceManager")
-        pw.println("    sensorPropertiesInternal: ${faceManager?.sensorPropertiesInternal}")
-        pw.println(
-            "    supportsFaceDetection: " +
-                "${faceManager?.sensorPropertiesInternal?.firstOrNull()?.supportsFaceDetection}"
-        )
-        pw.println(
-            "  faceAuthRequestedWhileCancellation: ${faceAuthRequestedWhileCancellation?.reason}"
-        )
-        pw.println("  cancellationSignal: $cancellationSignal")
-        pw.println("  faceAcquiredInfoIgnoreList: $faceAcquiredInfoIgnoreList")
-        pw.println("  _authenticationStatus: ${_authenticationStatus.value}")
-        pw.println("  _detectionStatus: ${_detectionStatus.value}")
-        pw.println("  currentUserId: $currentUserId")
-        pw.println("  keyguardSessionId: $keyguardSessionId")
-        pw.println("  lockscreenBypassEnabled: $lockscreenBypassEnabled")
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt
new file mode 100644
index 0000000..3c66f24
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface KeyguardFaceAuthModule {
+    @Binds
+    fun deviceEntryFaceAuthRepository(
+        impl: DeviceEntryFaceAuthRepositoryImpl
+    ): DeviceEntryFaceAuthRepository
+
+    @Binds fun trustRepository(impl: TrustRepositoryImpl): TrustRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
index 33f4e2e..9212aa1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
@@ -27,7 +27,6 @@
 import com.android.keyguard.KeyguardSecurityModel
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.settingslib.Utils
 import com.android.systemui.DejankUtils
 import com.android.systemui.R
 import com.android.systemui.classifier.FalsingCollector
@@ -42,12 +41,12 @@
 import com.android.systemui.shared.system.SysUiStatsLog
 import com.android.systemui.statusbar.phone.KeyguardBypassController
 import com.android.systemui.statusbar.policy.KeyguardStateController
-import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.filter
 import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.map
+import javax.inject.Inject
 
 /**
  * Encapsulates business logic for interacting with the lock-screen primary (pin/pattern/password)
@@ -82,12 +81,6 @@
     /** Runnable to show the primary bouncer. */
     val showRunnable = Runnable {
         repository.setPrimaryShow(true)
-        primaryBouncerView.delegate?.showPromptReason(repository.bouncerPromptReason)
-        (repository.bouncerErrorMessage as? String)?.let {
-            repository.setShowMessage(
-                BouncerShowMessageModel(message = it, Utils.getColorError(context))
-            )
-        }
         repository.setPrimaryShowingSoon(false)
         primaryBouncerCallbackInteractor.dispatchVisibilityChanged(View.VISIBLE)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
index b1c5f8f..eded9c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
@@ -18,7 +18,10 @@
 
 import android.hardware.face.FaceManager
 
-/** Authentication status provided by [com.android.keyguard.faceauth.KeyguardFaceAuthManager] */
+/**
+ * Authentication status provided by
+ * [com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository]
+ */
 sealed class AuthenticationStatus
 
 /** Success authentication status. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
index 468a6b5..927df55 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -121,6 +121,7 @@
                                     securityContainerController.showPrimarySecurityScreen(
                                         /* turningOff= */ false
                                     )
+                                    securityContainerController.setInitialMessage()
                                     securityContainerController.appear()
                                     securityContainerController.onResume(
                                         KeyguardSecurityView.SCREEN_ON
diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
index 647e3a1..f7355d5 100644
--- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
@@ -7,17 +7,17 @@
 import com.android.systemui.log.dagger.FaceAuthLog
 import com.android.systemui.plugins.log.LogBuffer
 import com.android.systemui.plugins.log.LogLevel.DEBUG
-import com.google.errorprone.annotations.CompileTimeConstant
 import javax.inject.Inject
 
-private const val TAG = "KeyguardFaceAuthManagerLog"
+private const val TAG = "DeviceEntryFaceAuthRepositoryLog"
 
 /**
- * Helper class for logging for [com.android.keyguard.faceauth.KeyguardFaceAuthManager]
+ * Helper class for logging for
+ * [com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository]
  *
  * To enable logcat echoing for an entire buffer:
  * ```
- *   adb shell settings put global systemui/buffer/KeyguardFaceAuthManagerLog <logLevel>
+ *   adb shell settings put global systemui/buffer/DeviceEntryFaceAuthRepositoryLog <logLevel>
  *
  * ```
  */
@@ -82,8 +82,19 @@
         )
     }
 
-    fun skippingBecauseAlreadyRunning(@CompileTimeConstant operation: String) {
-        logBuffer.log(TAG, DEBUG, "isAuthRunning is true, skipping $operation")
+    fun skippingDetection(isAuthRunning: Boolean, detectCancellationNotNull: Boolean) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                bool1 = isAuthRunning
+                bool2 = detectCancellationNotNull
+            },
+            {
+                "Skipping running detection: isAuthRunning: $bool1, " +
+                    "detectCancellationNotNull: $bool2"
+            }
+        )
     }
 
     fun faceDetectionStarted() {
@@ -177,4 +188,33 @@
             { "Face authenticated successfully: userId: $int1, isStrongBiometric: $bool1" }
         )
     }
+
+    fun observedConditionChanged(newValue: Boolean, context: String) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            {
+                bool1 = newValue
+                str1 = context
+            },
+            { "Observed condition changed: $str1, new value: $bool1" }
+        )
+    }
+
+    fun canFaceAuthRunChanged(canRun: Boolean) {
+        logBuffer.log(TAG, DEBUG, { bool1 = canRun }, { "canFaceAuthRun value changed to $bool1" })
+    }
+
+    fun canRunDetectionChanged(canRunDetection: Boolean) {
+        logBuffer.log(
+            TAG,
+            DEBUG,
+            { bool1 = canRunDetection },
+            { "canRunDetection value changed to $bool1" }
+        )
+    }
+
+    fun cancellingFaceAuth() {
+        logBuffer.log(TAG, DEBUG, "cancelling face auth because a gating condition became false")
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/log/SessionTracker.java b/packages/SystemUI/src/com/android/systemui/log/SessionTracker.java
index b98a92f..d848b43 100644
--- a/packages/SystemUI/src/com/android/systemui/log/SessionTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/log/SessionTracker.java
@@ -28,6 +28,8 @@
 
 import com.android.internal.logging.InstanceId;
 import com.android.internal.logging.InstanceIdSequence;
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -60,6 +62,7 @@
     private final AuthController mAuthController;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final KeyguardStateController mKeyguardStateController;
+    private final UiEventLogger mUiEventLogger;
     private final Map<Integer, InstanceId> mSessionToInstanceId = new HashMap<>();
 
     private boolean mKeyguardSessionStarted;
@@ -69,12 +72,14 @@
             IStatusBarService statusBarService,
             AuthController authController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
-            KeyguardStateController keyguardStateController
+            KeyguardStateController keyguardStateController,
+            UiEventLogger uiEventLogger
     ) {
         mStatusBarManagerService = statusBarService;
         mAuthController = authController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mKeyguardStateController = keyguardStateController;
+        mUiEventLogger = uiEventLogger;
     }
 
     @Override
@@ -116,6 +121,10 @@
     }
 
     private void endSession(int type) {
+        endSession(type, null);
+    }
+
+    private void endSession(int type, @Nullable SessionUiEvent endSessionUiEvent) {
         if (mSessionToInstanceId.getOrDefault(type, null) == null) {
             Log.e(TAG, "session [" + getString(type) + "] was not started");
             return;
@@ -127,6 +136,9 @@
             if (DEBUG) {
                 Log.d(TAG, "Session end for [" + getString(type) + "] id=" + instanceId);
             }
+            if (endSessionUiEvent != null) {
+                mUiEventLogger.log(endSessionUiEvent, instanceId);
+            }
             mStatusBarManagerService.onSessionEnded(type, instanceId);
         } catch (RemoteException e) {
             Log.e(TAG, "Unable to send onSessionEnded for session="
@@ -139,7 +151,7 @@
         @Override
         public void onStartedGoingToSleep(int why) {
             if (mKeyguardSessionStarted) {
-                endSession(SESSION_KEYGUARD);
+                endSession(SESSION_KEYGUARD, SessionUiEvent.KEYGUARD_SESSION_END_GOING_TO_SLEEP);
             }
 
             // Start a new session whenever the device goes to sleep
@@ -162,7 +174,8 @@
                 startSession(SESSION_KEYGUARD);
             } else if (!keyguardShowing && wasSessionStarted) {
                 mKeyguardSessionStarted = false;
-                endSession(SESSION_KEYGUARD);
+                endSession(SESSION_KEYGUARD,
+                        SessionUiEvent.KEYGUARD_SESSION_END_KEYGUARD_GOING_AWAY);
             }
         }
     };
@@ -200,4 +213,22 @@
 
         return "unknownType=" + sessionType;
     }
+
+    enum SessionUiEvent implements UiEventLogger.UiEventEnum {
+        @UiEvent(doc = "A keyguard session ended due to the keyguard going away.")
+        KEYGUARD_SESSION_END_KEYGUARD_GOING_AWAY(1354),
+
+        @UiEvent(doc = "A keyguard session ended due to display going to sleep.")
+        KEYGUARD_SESSION_END_GOING_TO_SLEEP(1355);
+
+        private final int mId;
+        SessionUiEvent(int id) {
+            mId = id;
+        }
+
+        @Override
+        public int getId() {
+            return mId;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index e204def..3775e2c 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -367,13 +367,13 @@
 
     /**
      * Provides a {@link LogBuffer} for use by
-     *  {@link com.android.keyguard.faceauth.KeyguardFaceAuthManagerImpl}.
+     *  {@link com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepositoryImpl}.
      */
     @Provides
     @SysUISingleton
     @FaceAuthLog
     public static LogBuffer provideFaceAuthLog(LogBufferFactory factory) {
-        return factory.create("KeyguardFaceAuthManagerLog", 300);
+        return factory.create("DeviceEntryFaceAuthRepositoryLog", 300);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index f92a5ab..731bb2f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -66,6 +66,8 @@
 
     protected final MediaOutputController mController;
 
+    private static final int UNMUTE_DEFAULT_VOLUME = 2;
+
     Context mContext;
     View mHolderView;
     boolean mIsDragging;
@@ -193,10 +195,6 @@
             mTwoLineTitleText.setTextColor(mController.getColorItemContent());
             if (mController.isAdvancedLayoutSupported()) {
                 mVolumeValueText.setTextColor(mController.getColorItemContent());
-                mTitleIcon.setOnTouchListener(((v, event) -> {
-                    mSeekBar.dispatchTouchEvent(event);
-                    return false;
-                }));
             }
             mSeekBar.setProgressTintList(
                     ColorStateList.valueOf(mController.getColorSeekbarProgress()));
@@ -546,13 +544,21 @@
         private void enableSeekBar(MediaDevice device) {
             mSeekBar.setEnabled(true);
             mSeekBar.setOnTouchListener((v, event) -> false);
-            if (mController.isAdvancedLayoutSupported()) {
-                updateIconAreaClickListener((v) -> {
+            updateIconAreaClickListener((v) -> {
+                if (device.getCurrentVolume() == 0) {
+                    mController.adjustVolume(device, UNMUTE_DEFAULT_VOLUME);
+                    updateUnmutedVolumeIcon();
+                    mTitleIcon.setOnTouchListener(((iconV, event) -> false));
+                } else {
                     mSeekBar.resetVolume();
                     mController.adjustVolume(device, 0);
                     updateMutedVolumeIcon();
-                });
-            }
+                    mTitleIcon.setOnTouchListener(((iconV, event) -> {
+                        mSeekBar.dispatchTouchEvent(event);
+                        return false;
+                    }));
+                }
+            });
         }
 
         protected void setUpDeviceIcon(MediaDevice device) {
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index f5c0a94..334c70b 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -296,7 +296,8 @@
 
         // EXTRA_USE_STYLUS_MODE does not mean a stylus is in-use, but a stylus entrypoint
         // was used to start the note task.
-        putExtra(Intent.EXTRA_USE_STYLUS_MODE, true)
+        val useStylusMode = info.entryPoint != NoteTaskEntryPoint.KEYBOARD_SHORTCUT
+        putExtra(Intent.EXTRA_USE_STYLUS_MODE, useStylusMode)
 
         addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
         // We should ensure the note experience can be opened both as a full screen (lockscreen)
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
index 2fa8f9a..fae325c 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
@@ -25,7 +25,8 @@
  * An entry point represents where the note task has ben called from. In rare cases, it may
  * represent a "re-entry" (i.e., [APP_CLIPS]).
  */
-enum class NoteTaskEntryPoint {
+enum class
+NoteTaskEntryPoint {
 
     /** @see [LaunchNoteTaskActivity] */
     WIDGET_PICKER_SHORTCUT,
@@ -38,4 +39,7 @@
 
     /** @see [AppClipsTrampolineActivity] */
     APP_CLIPS,
+
+    /** @see [NoteTaskInitializer.callbacks] */
+    KEYBOARD_SHORTCUT,
 }
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt
index 16dd16e..48a5933 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt
@@ -18,6 +18,7 @@
 import com.android.internal.logging.UiEvent
 import com.android.internal.logging.UiEventLogger
 import com.android.systemui.notetask.NoteTaskEntryPoint.APP_CLIPS
+import com.android.systemui.notetask.NoteTaskEntryPoint.KEYBOARD_SHORTCUT
 import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE
 import com.android.systemui.notetask.NoteTaskEntryPoint.TAIL_BUTTON
 import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT
@@ -51,6 +52,7 @@
                 WIDGET_PICKER_SHORTCUT -> NOTE_OPENED_VIA_SHORTCUT
                 QUICK_AFFORDANCE -> NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE
                 APP_CLIPS -> return
+                KEYBOARD_SHORTCUT -> return
                 null -> return
             }
         uiEventLogger.log(event, info.uid, info.packageName)
@@ -70,6 +72,7 @@
                 WIDGET_PICKER_SHORTCUT -> return
                 QUICK_AFFORDANCE -> return
                 APP_CLIPS -> return
+                KEYBOARD_SHORTCUT -> return
                 null -> return
             }
         uiEventLogger.log(event, info.uid, info.packageName)
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
index 04ed08b..23ee13b 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
@@ -41,9 +41,12 @@
     @VisibleForTesting
     val callbacks =
         object : CommandQueue.Callbacks {
-            override fun handleSystemKey(keyCode: Int) {
-                if (keyCode == KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL) {
+            override fun handleSystemKey(key: KeyEvent) {
+                if (key.keyCode == KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL) {
                     controller.showNoteTask(NoteTaskEntryPoint.TAIL_BUTTON)
+                } else if (key.keyCode == KeyEvent.KEYCODE_N && key.isMetaPressed &&
+                        key.isCtrlPressed) {
+                    controller.showNoteTask(NoteTaskEntryPoint.KEYBOARD_SHORTCUT)
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
index 5355865..0641eec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -47,7 +47,6 @@
 import androidx.recyclerview.widget.DiffUtil
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_INFORM_JOB_SCHEDULER_OF_PENDING_APP_STOP
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_FOOTER_DOT
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS
@@ -80,8 +79,6 @@
 
 /** A controller for the dealing with services running in the foreground. */
 interface FgsManagerController {
-    /** Whether the TaskManager (and therefore this controller) is actually available. */
-    val isAvailable: StateFlow<Boolean>
 
     /** The number of packages with a service running in the foreground. */
     val numRunningPackages: Int
@@ -155,7 +152,6 @@
 
     companion object {
         private const val INTERACTION_JANK_TAG = "active_background_apps"
-        private const val DEFAULT_TASK_MANAGER_ENABLED = true
         private const val DEFAULT_TASK_MANAGER_SHOW_FOOTER_DOT = false
         private const val DEFAULT_TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS = true
         private const val DEFAULT_TASK_MANAGER_SHOW_USER_VISIBLE_JOBS = true
@@ -165,9 +161,6 @@
     override var newChangesSinceDialogWasDismissed = false
         private set
 
-    val _isAvailable = MutableStateFlow(false)
-    override val isAvailable: StateFlow<Boolean> = _isAvailable.asStateFlow()
-
     val _showFooterDot = MutableStateFlow(false)
     override val showFooterDot: StateFlow<Boolean> = _showFooterDot.asStateFlow()
 
@@ -264,7 +257,6 @@
                 NAMESPACE_SYSTEMUI,
                 backgroundExecutor
             ) {
-                _isAvailable.value = it.getBoolean(TASK_MANAGER_ENABLED, _isAvailable.value)
                 _showFooterDot.value =
                     it.getBoolean(TASK_MANAGER_SHOW_FOOTER_DOT, _showFooterDot.value)
                 showStopBtnForUserAllowlistedApps = it.getBoolean(
@@ -280,11 +272,6 @@
                     TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS,
                     informJobSchedulerOfPendingAppStop)
             }
-
-            _isAvailable.value = deviceConfigProxy.getBoolean(
-                NAMESPACE_SYSTEMUI,
-                TASK_MANAGER_ENABLED, DEFAULT_TASK_MANAGER_ENABLED
-            )
             _showFooterDot.value = deviceConfigProxy.getBoolean(
                 NAMESPACE_SYSTEMUI,
                 TASK_MANAGER_SHOW_FOOTER_DOT, DEFAULT_TASK_MANAGER_SHOW_FOOTER_DOT
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index 6be74a0..ce690e2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -26,6 +26,7 @@
 import com.android.systemui.plugins.qs.QSFactory;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTileView;
+import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository;
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
 import com.android.systemui.util.leak.GarbageMonitor;
 
@@ -34,7 +35,7 @@
 import java.util.Collection;
 import java.util.List;
 
-public interface QSHost extends PanelInteractor {
+public interface QSHost extends PanelInteractor, CustomTileAddedRepository {
     String TILES_SETTING = Settings.Secure.QS_TILES;
     int POSITION_AT_END = -1;
 
@@ -102,9 +103,6 @@
     void removeTileByUser(ComponentName tile);
     void changeTilesByUser(List<String> previousTiles, List<String> newTiles);
 
-    boolean isTileAdded(ComponentName componentName, int userId);
-    void setTileAdded(ComponentName componentName, int userId, boolean added);
-
     int indexOf(String tileSpec);
 
     InstanceId getNewInstanceId();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
index 958fa71..964fe71 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
@@ -20,6 +20,8 @@
 import com.android.systemui.flags.Flags
 import com.android.systemui.qs.QSHost
 import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository
+import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedSharedPrefsRepository
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractorImpl
 import dagger.Binds
@@ -46,5 +48,19 @@
                 qsHost
             }
         }
+
+        @Provides
+        @JvmStatic
+        fun provideCustomTileAddedRepository(
+            featureFlags: FeatureFlags,
+            qsHost: QSHost,
+            customTileAddedRepository: CustomTileAddedSharedPrefsRepository
+        ): CustomTileAddedRepository {
+            return if (featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)) {
+                customTileAddedRepository
+            } else {
+                qsHost
+            }
+        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
index 9f93e49..7a10a27 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -33,6 +33,7 @@
 
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener;
+import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository;
 import com.android.systemui.settings.UserTracker;
 
 import java.util.List;
@@ -59,6 +60,7 @@
     private final TileLifecycleManager mStateManager;
     private final Handler mHandler;
     private final UserTracker mUserTracker;
+    private final CustomTileAddedRepository mCustomTileAddedRepository;
     private boolean mBindRequested;
     private boolean mBindAllowed;
     private boolean mBound;
@@ -72,9 +74,10 @@
     private boolean mStarted = false;
 
     TileServiceManager(TileServices tileServices, Handler handler, ComponentName component,
-            BroadcastDispatcher broadcastDispatcher, UserTracker userTracker) {
-        this(tileServices, handler, userTracker, new TileLifecycleManager(handler,
-                tileServices.getContext(), tileServices,
+            BroadcastDispatcher broadcastDispatcher, UserTracker userTracker,
+            CustomTileAddedRepository customTileAddedRepository) {
+        this(tileServices, handler, userTracker, customTileAddedRepository,
+                new TileLifecycleManager(handler, tileServices.getContext(), tileServices,
                 new PackageManagerAdapter(tileServices.getContext()), broadcastDispatcher,
                 new Intent(TileService.ACTION_QS_TILE).setComponent(component),
                 userTracker.getUserHandle()));
@@ -82,11 +85,13 @@
 
     @VisibleForTesting
     TileServiceManager(TileServices tileServices, Handler handler, UserTracker userTracker,
+            CustomTileAddedRepository customTileAddedRepository,
             TileLifecycleManager tileLifecycleManager) {
         mServices = tileServices;
         mHandler = handler;
         mStateManager = tileLifecycleManager;
         mUserTracker = userTracker;
+        mCustomTileAddedRepository = customTileAddedRepository;
 
         IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -111,8 +116,8 @@
         mStarted = true;
         ComponentName component = mStateManager.getComponent();
         final int userId = mStateManager.getUserId();
-        if (!mServices.getHost().isTileAdded(component, userId)) {
-            mServices.getHost().setTileAdded(component, userId, true);
+        if (!mCustomTileAddedRepository.isTileAdded(component, userId)) {
+            mCustomTileAddedRepository.setTileAdded(component, userId, true);
             mStateManager.onTileAdded();
             mStateManager.flushMessagesAndUnbind();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index 42536fe..121955c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -41,6 +41,7 @@
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository;
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.CommandQueue;
@@ -77,6 +78,7 @@
     private final UserTracker mUserTracker;
     private final StatusBarIconController mStatusBarIconController;
     private final PanelInteractor mPanelInteractor;
+    private final CustomTileAddedRepository mCustomTileAddedRepository;
 
     private int mMaxBound = DEFAULT_MAX_BOUND;
 
@@ -89,7 +91,8 @@
             KeyguardStateController keyguardStateController,
             CommandQueue commandQueue,
             StatusBarIconController statusBarIconController,
-            PanelInteractor panelInteractor) {
+            PanelInteractor panelInteractor,
+            CustomTileAddedRepository customTileAddedRepository) {
         mHost = host;
         mKeyguardStateController = keyguardStateController;
         mContext = mHost.getContext();
@@ -101,6 +104,7 @@
         mStatusBarIconController = statusBarIconController;
         mCommandQueue.addCallback(mRequestListeningCallback);
         mPanelInteractor = panelInteractor;
+        mCustomTileAddedRepository = customTileAddedRepository;
     }
 
     public Context getContext() {
@@ -128,7 +132,7 @@
     protected TileServiceManager onCreateTileService(ComponentName component,
             BroadcastDispatcher broadcastDispatcher) {
         return new TileServiceManager(this, mHandlerProvider.get(), component,
-                broadcastDispatcher, mUserTracker);
+                broadcastDispatcher, mUserTracker, mCustomTileAddedRepository);
     }
 
     public void freeService(CustomTile tile, TileServiceManager service) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/ForegroundServicesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/ForegroundServicesRepository.kt
index 37a9c40..bd9d70c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/ForegroundServicesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/ForegroundServicesRepository.kt
@@ -32,8 +32,6 @@
 interface ForegroundServicesRepository {
     /**
      * The number of packages with a service running in the foreground.
-     *
-     * Note that this will be equal to 0 if [FgsManagerController.isAvailable] is false.
      */
     val foregroundServicesCount: Flow<Int>
 
@@ -52,32 +50,24 @@
     fgsManagerController: FgsManagerController,
 ) : ForegroundServicesRepository {
     override val foregroundServicesCount: Flow<Int> =
-        fgsManagerController.isAvailable
-            .flatMapLatest { isAvailable ->
-                if (!isAvailable) {
-                    return@flatMapLatest flowOf(0)
+            conflatedCallbackFlow<Int> {
+                fun updateState(numberOfPackages: Int) {
+                    trySendWithFailureLogging(numberOfPackages, TAG)
                 }
 
-                conflatedCallbackFlow {
-                    fun updateState(numberOfPackages: Int) {
-                        trySendWithFailureLogging(numberOfPackages, TAG)
-                    }
-
-                    val listener =
+                val listener =
                         object : FgsManagerController.OnNumberOfPackagesChangedListener {
                             override fun onNumberOfPackagesChanged(numberOfPackages: Int) {
                                 updateState(numberOfPackages)
                             }
                         }
 
-                    fgsManagerController.addOnNumberOfPackagesChangedListener(listener)
-                    updateState(fgsManagerController.numRunningPackages)
-                    awaitClose {
-                        fgsManagerController.removeOnNumberOfPackagesChangedListener(listener)
-                    }
+                fgsManagerController.addOnNumberOfPackagesChangedListener(listener)
+                updateState(fgsManagerController.numRunningPackages)
+                awaitClose {
+                    fgsManagerController.removeOnNumberOfPackagesChangedListener(listener)
                 }
-            }
-            .distinctUntilChanged()
+            }.distinctUntilChanged()
 
     override val hasNewChanges: Flow<Boolean> =
         fgsManagerController.showFooterDot.flatMapLatest { showFooterDot ->
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedRepository.kt
new file mode 100644
index 0000000..7fc906b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedRepository.kt
@@ -0,0 +1,65 @@
+/*
+ * 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.qs.pipeline.data.repository
+
+import android.content.ComponentName
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.settings.UserFileManager
+import javax.inject.Inject
+
+/**
+ * Repository for keeping track of whether a given [CustomTile] [ComponentName] has been added to
+ * the set of current tiles for a user. This is used to determine when lifecycle methods in
+ * `TileService` about the tile being added/removed need to be called.
+ */
+interface CustomTileAddedRepository {
+    /**
+     * Check if a particular [CustomTile] associated with [componentName] has been added for
+     * [userId] and has not been removed since.
+     */
+    fun isTileAdded(componentName: ComponentName, userId: Int): Boolean
+
+    /**
+     * Persists whether a particular [CustomTile] associated with [componentName] has been added and
+     * it's currently in the set of selected tiles for [userId].
+     */
+    fun setTileAdded(componentName: ComponentName, userId: Int, added: Boolean)
+}
+
+@SysUISingleton
+class CustomTileAddedSharedPrefsRepository
+@Inject
+constructor(private val userFileManager: UserFileManager) : CustomTileAddedRepository {
+
+    override fun isTileAdded(componentName: ComponentName, userId: Int): Boolean {
+        return userFileManager
+            .getSharedPreferences(TILES, 0, userId)
+            .getBoolean(componentName.flattenToString(), false)
+    }
+
+    override fun setTileAdded(componentName: ComponentName, userId: Int, added: Boolean) {
+        userFileManager
+            .getSharedPreferences(TILES, 0, userId)
+            .edit()
+            .putBoolean(componentName.flattenToString(), added)
+            .apply()
+    }
+
+    companion object {
+        private const val TILES = "tiles_prefs"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 3090b79..4a31998 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -496,7 +496,7 @@
         }
 
         // Colors
-        if (state.state != lastState || state.disabledByPolicy || lastDisabledByPolicy) {
+        if (state.state != lastState || state.disabledByPolicy != lastDisabledByPolicy) {
             singleAnimator.cancel()
             mQsLogger?.logTileBackgroundColorUpdateIfInternetTile(
                     state.spec,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 1b83397..90e31af 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -336,7 +336,8 @@
         @Override
         public void expandNotificationPanel() {
             verifyCallerAndClearCallingIdentity("expandNotificationPanel",
-                    () -> mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN));
+                    () -> mCommandQueue.handleSystemKey(new KeyEvent(KeyEvent.ACTION_DOWN,
+                            KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN)));
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index a9af1a2..6f85c45 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -44,7 +44,6 @@
 import android.app.Notification;
 import android.app.assist.AssistContent;
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -525,39 +524,6 @@
                 mWindowManager.getCurrentWindowMetrics().getWindowInsets());
     }
 
-    @MainThread
-    void takeScreenshotFullscreen(ComponentName topComponent, Consumer<Uri> finisher,
-            RequestCallback requestCallback) {
-        Assert.isMainThread();
-        mCurrentRequestCallback = requestCallback;
-        takeScreenshotInternal(topComponent, finisher, getFullScreenRect());
-    }
-
-    @MainThread
-    void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
-            Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
-            Consumer<Uri> finisher, RequestCallback requestCallback) {
-        Assert.isMainThread();
-        if (screenshot == null) {
-            Log.e(TAG, "Got null bitmap from screenshot message");
-            mNotificationsController.notifyScreenshotError(
-                    R.string.screenshot_failed_to_capture_text);
-            requestCallback.reportError();
-            return;
-        }
-
-        boolean showFlash = false;
-        if (screenshotScreenBounds == null
-                || !aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) {
-            showFlash = true;
-            visibleInsets = Insets.NONE;
-            screenshotScreenBounds = new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight());
-        }
-        mCurrentRequestCallback = requestCallback;
-        saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, topComponent,
-                showFlash, UserHandle.of(userId));
-    }
-
     /**
      * Clears current screenshot
      */
@@ -695,103 +661,6 @@
         setContentView(mScreenshotView);
     }
 
-    /**
-     * Takes a screenshot of the current display and shows an animation.
-     */
-    private void takeScreenshotInternal(ComponentName topComponent, Consumer<Uri> finisher,
-            Rect crop) {
-        mScreenshotTakenInPortrait =
-                mContext.getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;
-
-        // copy the input Rect, since SurfaceControl.screenshot can mutate it
-        Rect screenRect = new Rect(crop);
-        Bitmap screenshot = mImageCapture.captureDisplay(mDisplayTracker.getDefaultDisplayId(),
-                crop);
-
-        if (screenshot == null) {
-            Log.e(TAG, "takeScreenshotInternal: Screenshot bitmap was null");
-            mNotificationsController.notifyScreenshotError(
-                    R.string.screenshot_failed_to_capture_text);
-            if (mCurrentRequestCallback != null) {
-                mCurrentRequestCallback.reportError();
-            }
-            return;
-        }
-
-        saveScreenshot(screenshot, finisher, screenRect, Insets.NONE, topComponent, true,
-                Process.myUserHandle());
-
-        mBroadcastSender.sendBroadcast(new Intent(ClipboardOverlayController.SCREENSHOT_ACTION),
-                ClipboardOverlayController.SELF_PERMISSION);
-    }
-
-    private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
-            Insets screenInsets, ComponentName topComponent, boolean showFlash, UserHandle owner) {
-        withWindowAttached(() -> {
-            if (mUserManager.isManagedProfile(owner.getIdentifier())) {
-                mScreenshotView.announceForAccessibility(mContext.getResources().getString(
-                        R.string.screenshot_saving_work_profile_title));
-            } else {
-                mScreenshotView.announceForAccessibility(
-                        mContext.getResources().getString(R.string.screenshot_saving_title));
-            }
-        });
-
-        mScreenshotView.reset();
-
-        if (mScreenshotView.isAttachedToWindow()) {
-            // if we didn't already dismiss for another reason
-            if (!mScreenshotView.isDismissing()) {
-                mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED, 0, mPackageName);
-            }
-            if (DEBUG_WINDOW) {
-                Log.d(TAG, "saveScreenshot: screenshotView is already attached, resetting. "
-                        + "(dismissing=" + mScreenshotView.isDismissing() + ")");
-            }
-        }
-        mPackageName = topComponent == null ? "" : topComponent.getPackageName();
-        mScreenshotView.setPackageName(mPackageName);
-
-        mScreenshotView.updateOrientation(
-                mWindowManager.getCurrentWindowMetrics().getWindowInsets());
-
-        mScreenBitmap = screenshot;
-
-        if (!isUserSetupComplete(owner)) {
-            Log.w(TAG, "User setup not complete, displaying toast only");
-            // User setup isn't complete, so we don't want to show any UI beyond a toast, as editing
-            // and sharing shouldn't be exposed to the user.
-            saveScreenshotAndToast(owner, finisher);
-            return;
-        }
-
-        // Optimizations
-        mScreenBitmap.setHasAlpha(false);
-        mScreenBitmap.prepareToDraw();
-
-        saveScreenshotInWorkerThread(owner, finisher, this::showUiOnActionsReady,
-                this::showUiOnQuickShareActionReady);
-
-        // The window is focusable by default
-        setWindowFocusable(true);
-        mScreenshotView.requestFocus();
-
-        enqueueScrollCaptureRequest(owner);
-
-        attachWindow();
-        prepareAnimation(screenRect, showFlash, () -> {
-            mMessageContainerController.onScreenshotTaken(owner);
-        });
-
-        mScreenshotView.badgeScreenshot(mContext.getPackageManager().getUserBadgedIcon(
-                mContext.getDrawable(R.drawable.overlay_badge_background), owner));
-        mScreenshotView.setScreenshot(mScreenBitmap, screenInsets);
-        // ignore system bar insets for the purpose of window layout
-        mWindow.getDecorView().setOnApplyWindowInsetsListener(
-                (v, insets) -> WindowInsets.CONSUMED);
-        mScreenshotHandler.cancelTimeout(); // restarted after animation
-    }
-
     private void prepareAnimation(Rect screenRect, boolean showFlash,
             Runnable onAnimationComplete) {
         mScreenshotView.getViewTreeObserver().addOnPreDrawListener(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index f3d2828..1cdad83 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -36,9 +36,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.graphics.Bitmap;
-import android.graphics.Insets;
-import android.graphics.Rect;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.IBinder;
@@ -49,7 +46,6 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.Log;
-import android.view.WindowManager;
 import android.widget.Toast;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -58,7 +54,6 @@
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -222,30 +217,17 @@
             return;
         }
 
-        if (mFeatureFlags.isEnabled(Flags.SCREENSHOT_METADATA_REFACTOR)) {
-            Log.d(TAG, "Processing screenshot data");
-            ScreenshotData screenshotData = ScreenshotData.fromRequest(request);
-            try {
-                mProcessor.processAsync(screenshotData,
-                        (data) -> dispatchToController(data, onSaved, callback));
-            } catch (IllegalStateException e) {
-                Log.e(TAG, "Failed to process screenshot request!", e);
-                logFailedRequest(request);
-                mNotificationsController.notifyScreenshotError(
-                        R.string.screenshot_failed_to_capture_text);
-                callback.reportError();
-            }
-        } else {
-            try {
-                mProcessor.processAsync(request,
-                        (r) -> dispatchToController(r, onSaved, callback));
-            } catch (IllegalStateException e) {
-                Log.e(TAG, "Failed to process screenshot request!", e);
-                logFailedRequest(request);
-                mNotificationsController.notifyScreenshotError(
-                        R.string.screenshot_failed_to_capture_text);
-                callback.reportError();
-            }
+        Log.d(TAG, "Processing screenshot data");
+        ScreenshotData screenshotData = ScreenshotData.fromRequest(request);
+        try {
+            mProcessor.processAsync(screenshotData,
+                    (data) -> dispatchToController(data, onSaved, callback));
+        } catch (IllegalStateException e) {
+            Log.e(TAG, "Failed to process screenshot request!", e);
+            logFailedRequest(request);
+            mNotificationsController.notifyScreenshotError(
+                    R.string.screenshot_failed_to_capture_text);
+            callback.reportError();
         }
     }
 
@@ -257,38 +239,6 @@
         mScreenshot.handleScreenshot(screenshot, uriConsumer, callback);
     }
 
-    private void dispatchToController(ScreenshotRequest request,
-            Consumer<Uri> uriConsumer, RequestCallback callback) {
-        ComponentName topComponent = request.getTopComponent();
-        String packageName = topComponent == null ? "" : topComponent.getPackageName();
-        mUiEventLogger.log(
-                ScreenshotEvent.getScreenshotSource(request.getSource()), 0, packageName);
-
-        switch (request.getType()) {
-            case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
-                if (DEBUG_SERVICE) {
-                    Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_FULLSCREEN");
-                }
-                mScreenshot.takeScreenshotFullscreen(topComponent, uriConsumer, callback);
-                break;
-            case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE:
-                if (DEBUG_SERVICE) {
-                    Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_PROVIDED_IMAGE");
-                }
-                Bitmap screenshot = request.getBitmap();
-                Rect screenBounds = request.getBoundsInScreen();
-                Insets insets = request.getInsets();
-                int taskId = request.getTaskId();
-                int userId = request.getUserId();
-
-                mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,
-                        taskId, userId, topComponent, uriConsumer, callback);
-                break;
-            default:
-                Log.wtf(TAG, "Invalid screenshot option: " + request.getType());
-        }
-    }
-
     private void logFailedRequest(ScreenshotRequest request) {
         ComponentName topComponent = request.getTopComponent();
         String packageName = topComponent == null ? "" : topComponent.getPackageName();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index c435799..fb4feb8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -53,6 +53,7 @@
 import android.os.RemoteException;
 import android.util.Pair;
 import android.util.SparseArray;
+import android.view.KeyEvent;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowInsetsController.Appearance;
 import android.view.WindowInsetsController.Behavior;
@@ -302,7 +303,7 @@
         default void remQsTile(ComponentName tile) { }
         default void clickTile(ComponentName tile) { }
 
-        default void handleSystemKey(int arg1) { }
+        default void handleSystemKey(KeyEvent arg1) { }
         default void showPinningEnterExitToast(boolean entering) { }
         default void showPinningEscapeToast() { }
         default void handleShowGlobalActionsMenu() { }
@@ -891,9 +892,9 @@
     }
 
     @Override
-    public void handleSystemKey(int key) {
+    public void handleSystemKey(KeyEvent key) {
         synchronized (mLock) {
-            mHandler.obtainMessage(MSG_HANDLE_SYSTEM_KEY, key, 0).sendToTarget();
+            mHandler.obtainMessage(MSG_HANDLE_SYSTEM_KEY, key).sendToTarget();
         }
     }
 
@@ -1534,7 +1535,7 @@
                     break;
                 case MSG_HANDLE_SYSTEM_KEY:
                     for (int i = 0; i < mCallbacks.size(); i++) {
-                        mCallbacks.get(i).handleSystemKey(msg.arg1);
+                        mCallbacks.get(i).handleSystemKey((KeyEvent) msg.obj);
                     }
                     break;
                 case MSG_SHOW_GLOBAL_ACTIONS:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 9272f06..0195d45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -157,7 +157,8 @@
         if (animationAdapter != null) {
             if (ENABLE_SHELL_TRANSITIONS) {
                 options = ActivityOptions.makeRemoteTransition(
-                        RemoteTransitionAdapter.adaptRemoteAnimation(animationAdapter));
+                        RemoteTransitionAdapter.adaptRemoteAnimation(animationAdapter,
+                                "SysUILaunch"));
             } else {
                 options = ActivityOptions.makeRemoteAnimation(animationAdapter);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index e776015..8b6617b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -308,7 +308,7 @@
      * settings. Down action closes the entire panel.
      */
     @Override
-    public void handleSystemKey(int key) {
+    public void handleSystemKey(KeyEvent key) {
         if (CentralSurfaces.SPEW) {
             Log.d(CentralSurfaces.TAG, "handleNavigationKey: " + key);
         }
@@ -320,11 +320,11 @@
         // Panels are not available in setup
         if (!mDeviceProvisionedController.isCurrentUserSetup()) return;
 
-        if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP == key) {
+        if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP == key.getKeyCode()) {
             mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_UP);
             mNotificationPanelViewController.collapse(
                     false /* delayed */, 1.0f /* speedUpFactor */);
-        } else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key) {
+        } else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key.getKeyCode()) {
             mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_DOWN);
             if (mNotificationPanelViewController.isFullyCollapsed()) {
                 if (mVibrateOnOpening) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
index c817466..b303151 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
@@ -85,16 +85,17 @@
 
     /**
      * Called when keyguard is about to be displayed and allows to perform custom animation
+     *
+     * @return A handle that can be used for cancelling the animation, if necessary
      */
-    fun animateInKeyguard(keyguardView: View, after: Runnable) =
-        animations.firstOrNull {
+    fun animateInKeyguard(keyguardView: View, after: Runnable): AnimatorHandle? {
+        animations.forEach {
             if (it.shouldAnimateInKeyguard()) {
-                it.animateInKeyguard(keyguardView, after)
-                true
-            } else {
-                false
+                return@animateInKeyguard it.animateInKeyguard(keyguardView, after)
             }
         }
+        return null
+    }
 
     /**
      * If returns true it will disable propagating touches to apps and keyguard
@@ -211,7 +212,10 @@
     fun onAlwaysOnChanged(alwaysOn: Boolean) {}
 
     fun shouldAnimateInKeyguard(): Boolean = false
-    fun animateInKeyguard(keyguardView: View, after: Runnable) = after.run()
+    fun animateInKeyguard(keyguardView: View, after: Runnable): AnimatorHandle? {
+        after.run()
+        return null
+    }
 
     fun shouldDelayKeyguardShow(): Boolean = false
     fun isKeyguardShowDelayed(): Boolean = false
@@ -224,3 +228,7 @@
     fun shouldAnimateDozingChange(): Boolean = true
     fun shouldAnimateClockChange(): Boolean = true
 }
+
+interface AnimatorHandle {
+    fun cancel()
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 118bfc5..deb0414 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -160,7 +160,7 @@
      * Animates in the provided keyguard view, ending in the same position that it will be in on
      * AOD.
      */
-    override fun animateInKeyguard(keyguardView: View, after: Runnable) {
+    override fun animateInKeyguard(keyguardView: View, after: Runnable): AnimatorHandle {
         shouldAnimateInKeyguard = false
         keyguardView.alpha = 0f
         keyguardView.visibility = View.VISIBLE
@@ -175,11 +175,36 @@
         // We animate the Y properly separately using the PropertyAnimator, as the panel
         // view also needs to update the end position.
         PropertyAnimator.cancelAnimation(keyguardView, AnimatableProperty.Y)
-        PropertyAnimator.setProperty<View>(keyguardView, AnimatableProperty.Y, currentY,
-                AnimationProperties().setDuration(duration.toLong()),
-                true /* animate */)
 
-        keyguardView.animate()
+        // Start the animation on the next frame using Choreographer APIs. animateInKeyguard() is
+        // called while the system is busy processing lots of requests, so delaying the animation a
+        // frame will mitigate jank. In the event the animation is cancelled before the next frame
+        // is called, this callback will be removed
+        val keyguardAnimator = keyguardView.animate()
+        val nextFrameCallback = TraceUtils.namedRunnable("startAnimateInKeyguard") {
+            PropertyAnimator.setProperty(keyguardView, AnimatableProperty.Y, currentY,
+                    AnimationProperties().setDuration(duration.toLong()),
+                    true /* animate */)
+            keyguardAnimator.start()
+        }
+        DejankUtils.postAfterTraversal(nextFrameCallback)
+        val animatorHandle = object : AnimatorHandle {
+            private var hasCancelled = false
+            override fun cancel() {
+                if (!hasCancelled) {
+                    DejankUtils.removeCallbacks(nextFrameCallback)
+                    // If we're cancelled, reset state flags/listeners. The end action above
+                    // will not be called, which is what we want since that will finish the
+                    // screen off animation and show the lockscreen, which we don't want if we
+                    // were cancelled.
+                    aodUiAnimationPlaying = false
+                    decidedToAnimateGoingToSleep = null
+                    keyguardView.animate().setListener(null)
+                    hasCancelled = true
+                }
+            }
+        }
+        keyguardAnimator
                 .setDuration(duration.toLong())
                 .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
                 .alpha(1f)
@@ -205,14 +230,7 @@
                 }
                 .setListener(object : AnimatorListenerAdapter() {
                     override fun onAnimationCancel(animation: Animator?) {
-                        // If we're cancelled, reset state flags/listeners. The end action above
-                        // will not be called, which is what we want since that will finish the
-                        // screen off animation and show the lockscreen, which we don't want if we
-                        // were cancelled.
-                        aodUiAnimationPlaying = false
-                        decidedToAnimateGoingToSleep = null
-                        keyguardView.animate().setListener(null)
-
+                        animatorHandle.cancel()
                         interactionJankMonitor.cancel(CUJ_SCREEN_OFF_SHOW_AOD)
                     }
 
@@ -222,7 +240,7 @@
                                 CUJ_SCREEN_OFF_SHOW_AOD)
                     }
                 })
-                .start()
+        return animatorHandle
     }
 
     override fun onStartedWakingUp() {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index bd60401..e492534 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -295,8 +295,8 @@
             @Override
             public void notifyExpandNotification() {
                 mSysUiMainExecutor.execute(
-                        () -> mCommandQueue.handleSystemKey(
-                                KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN));
+                        () -> mCommandQueue.handleSystemKey(new KeyEvent(KeyEvent.ACTION_DOWN,
+                                KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN)));
             }
         });
 
diff --git a/packages/SystemUI/tests/res/drawable-nodpi/romainguy_rockaway.jpg b/packages/SystemUI/tests/res/drawable-nodpi/romainguy_rockaway.jpg
new file mode 100644
index 0000000..68473ba
--- /dev/null
+++ b/packages/SystemUI/tests/res/drawable-nodpi/romainguy_rockaway.jpg
Binary files differ
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index 7ce2b1c..1ba9931 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -152,19 +152,16 @@
                 false);
     }
 
+
     @Test
     public void testReset() {
         mKeyguardAbsKeyInputViewController.reset();
         verify(mKeyguardMessageAreaController).setMessage("", false);
-        verify(mAbsKeyInputView).resetPasswordText(false, false);
-        verify(mLockPatternUtils).getLockoutAttemptDeadline(anyInt());
     }
 
     @Test
-    public void onResume_Reset() {
+    public void testResume() {
         mKeyguardAbsKeyInputViewController.onResume(KeyguardSecurityView.VIEW_REVEALED);
-        verify(mKeyguardMessageAreaController).setMessage("", false);
-        verify(mAbsKeyInputView).resetPasswordText(false, false);
         verify(mLockPatternUtils).getLockoutAttemptDeadline(anyInt());
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index 6ae28b7..a8d5569 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -122,22 +122,8 @@
   }
 
   @Test
-  fun reset() {
-    mKeyguardPatternViewController.reset()
-    verify(mLockPatternView).setInStealthMode(anyBoolean())
-    verify(mLockPatternView).enableInput()
-    verify(mLockPatternView).setEnabled(true)
-    verify(mLockPatternView).clearPattern()
-    verify(mLockPatternUtils).getLockoutAttemptDeadline(anyInt())
-  }
-
-  @Test
   fun resume() {
     mKeyguardPatternViewController.onResume(KeyguardSecurityView.VIEW_REVEALED)
-    verify(mLockPatternView).setInStealthMode(anyBoolean())
-    verify(mLockPatternView).enableInput()
-    verify(mLockPatternView).setEnabled(true)
-    verify(mLockPatternView).clearPattern()
     verify(mLockPatternUtils).getLockoutAttemptDeadline(anyInt())
   }
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
index 33f0ae5..b6287598 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
@@ -141,27 +141,6 @@
     }
 
     @Test
-    public void testUnlockIconShows_biometricUnlockedTrue() {
-        // GIVEN UDFPS sensor location is available
-        setupUdfps();
-
-        // GIVEN lock icon controller is initialized and view is attached
-        init(/* useMigrationFlag= */false);
-        captureKeyguardUpdateMonitorCallback();
-
-        // GIVEN user has unlocked with a biometric auth (ie: face auth)
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
-        reset(mLockIconView);
-
-        // WHEN face auth's biometric running state changes
-        mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
-                BiometricSourceType.FACE);
-
-        // THEN the unlock icon is shown
-        verify(mLockIconView).setContentDescription(UNLOCKED_LABEL);
-    }
-
-    @Test
     public void testLockIconStartState() {
         // GIVEN lock icon state
         setupShowLockIcon();
@@ -268,27 +247,6 @@
     }
 
     @Test
-    public void lockIconShows_afterBiometricsCleared() {
-        // GIVEN lock icon controller is initialized and view is attached
-        init(/* useMigrationFlag= */false);
-        captureKeyguardUpdateMonitorCallback();
-
-        // GIVEN user has unlocked with a biometric auth (ie: face auth)
-        // and biometric running state changes
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
-        mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
-                BiometricSourceType.FACE);
-        reset(mLockIconView);
-
-        // WHEN biometrics are cleared
-        when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false);
-        mKeyguardUpdateMonitorCallback.onBiometricsCleared();
-
-        // THEN the lock icon is shown
-        verify(mLockIconView).setContentDescription(LOCKED_LABEL);
-    }
-
-    @Test
     public void lockIconShows_afterUnlockStateChanges() {
         // GIVEN lock icon controller is initialized and view is attached
         init(/* useMigrationFlag= */false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
index 236384b..4ea9616 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
@@ -32,6 +32,7 @@
 import org.junit.runner.RunWith
 import org.mockito.Mock
 import org.mockito.Mockito.never
+import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
@@ -161,6 +162,7 @@
         }
 
         verify(controlsModelCallback).onFirstChange()
+        verify(controlsModelCallback).onChange()
     }
 
     @Test
@@ -176,6 +178,7 @@
         )
 
         verify(controlsModelCallback).onFirstChange()
+        verify(controlsModelCallback).onChange()
     }
 
     @Test
@@ -191,6 +194,7 @@
         }
 
         verify(controlsModelCallback, never()).onFirstChange()
+        verify(controlsModelCallback, never()).onChange()
     }
 
     @Test
@@ -207,6 +211,7 @@
         }
 
         verify(controlsModelCallback).onFirstChange()
+        verify(controlsModelCallback).onChange()
     }
 
     @Test
@@ -222,6 +227,7 @@
         )
 
         verify(controlsModelCallback).onFirstChange()
+        verify(controlsModelCallback).onChange()
     }
 
     @Test
@@ -236,5 +242,24 @@
         }
 
         verify(controlsModelCallback, never()).onFirstChange()
+        verify(controlsModelCallback, never()).onChange()
+    }
+
+    @Test
+    fun testAddSecondChange_callbacks() {
+        model.changeFavoriteStatus("${idPrefix}4", true)
+        model.changeFavoriteStatus("${idPrefix}5", true)
+
+        verify(controlsModelCallback).onFirstChange()
+        verify(controlsModelCallback, times(2)).onChange()
+    }
+
+    @Test
+    fun testRemoveSecondChange_callbacks() {
+        model.changeFavoriteStatus("${idPrefix}1", false)
+        model.changeFavoriteStatus("${idPrefix}3", false)
+
+        verify(controlsModelCallback).onFirstChange()
+        verify(controlsModelCallback, times(2)).onChange()
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
index 3b6f7d1..4210675 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
@@ -2,27 +2,33 @@
 
 import android.content.ComponentName
 import android.content.Intent
+import android.os.Bundle
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import android.view.View
+import android.widget.Button
 import android.window.OnBackInvokedCallback
 import android.window.OnBackInvokedDispatcher
 import androidx.test.filters.SmallTest
 import androidx.test.rule.ActivityTestRule
 import androidx.test.runner.intercepting.SingleActivityFactory
+import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.controls.CustomIconCache
 import com.android.systemui.controls.controller.ControlsControllerImpl
-import com.android.systemui.controls.ui.ControlsUiController
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.settings.UserTracker
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
 import java.util.concurrent.CountDownLatch
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.eq
 import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito.verify
@@ -32,7 +38,15 @@
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper
 class ControlsEditingActivityTest : SysuiTestCase() {
+
+    private companion object {
+        val TEST_COMPONENT = ComponentName("TestPackageName", "TestClassName")
+        val TEST_STRUCTURE: CharSequence = "TestStructure"
+        val TEST_APP: CharSequence = "TestApp"
+    }
+
     private val uiExecutor = FakeExecutor(FakeSystemClock())
+    private val featureFlags = FakeFeatureFlags()
 
     @Mock lateinit var controller: ControlsControllerImpl
 
@@ -40,9 +54,6 @@
 
     @Mock lateinit var customIconCache: CustomIconCache
 
-    @Mock lateinit var uiController: ControlsUiController
-
-    private lateinit var controlsEditingActivity: ControlsEditingActivity_Factory
     private var latch: CountDownLatch = CountDownLatch(1)
 
     @Mock private lateinit var mockDispatcher: OnBackInvokedDispatcher
@@ -58,11 +69,11 @@
                 ) {
                 override fun create(intent: Intent?): TestableControlsEditingActivity {
                     return TestableControlsEditingActivity(
+                        featureFlags,
                         uiExecutor,
                         controller,
                         userTracker,
                         customIconCache,
-                        uiController,
                         mockDispatcher,
                         latch
                     )
@@ -75,19 +86,17 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        val intent = Intent()
-        intent.putExtra(ControlsEditingActivity.EXTRA_STRUCTURE, "TestTitle")
-        val cname = ComponentName("TestPackageName", "TestClassName")
-        intent.putExtra(Intent.EXTRA_COMPONENT_NAME, cname)
-        activityRule.launchActivity(intent)
+
+        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, false)
     }
 
     @Test
     fun testBackCallbackRegistrationAndUnregistration() {
+        launchActivity()
         // 1. ensure that launching the activity results in it registering a callback
         verify(mockDispatcher)
             .registerOnBackInvokedCallback(
-                ArgumentMatchers.eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
+                eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
                 captureCallback.capture()
             )
         activityRule.finishActivity()
@@ -96,15 +105,102 @@
         verify(mockDispatcher).unregisterOnBackInvokedCallback(captureCallback.value)
     }
 
-    public class TestableControlsEditingActivity(
-        private val executor: FakeExecutor,
-        private val controller: ControlsControllerImpl,
-        private val userTracker: UserTracker,
-        private val customIconCache: CustomIconCache,
-        private val uiController: ControlsUiController,
+    @Test
+    fun testNewFlowDisabled_addControlsButton_gone() {
+        with(launchActivity()) {
+            val addControlsButton = requireViewById<Button>(R.id.addControls)
+            assertThat(addControlsButton.visibility).isEqualTo(View.GONE)
+        }
+    }
+
+    @Test
+    fun testNewFlowEnabled_addControlsButton_visible() {
+        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
+        with(launchActivity()) {
+            val addControlsButton = requireViewById<Button>(R.id.addControls)
+            assertThat(addControlsButton.visibility).isEqualTo(View.VISIBLE)
+            assertThat(addControlsButton.isEnabled).isTrue()
+        }
+    }
+
+    @Test
+    fun testNotLaunchFromFavoriting_saveButton_disabled() {
+        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
+        with(launchActivity(isFromFavoriting = false)) {
+            val saveButton = requireViewById<Button>(R.id.done)
+            assertThat(saveButton.isEnabled).isFalse()
+        }
+    }
+
+    @Test
+    fun testLaunchFromFavoriting_saveButton_enabled() {
+        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
+        with(launchActivity(isFromFavoriting = true)) {
+            val saveButton = requireViewById<Button>(R.id.done)
+            assertThat(saveButton.isEnabled).isTrue()
+        }
+    }
+
+    @Test
+    fun testNotFromFavoriting_addControlsPressed_launchesFavouriting() {
+        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
+        with(launchActivity(isFromFavoriting = false)) {
+            val addControls = requireViewById<Button>(R.id.addControls)
+
+            activityRule.runOnUiThread { addControls.performClick() }
+
+            with(startActivityData!!.intent) {
+                assertThat(component)
+                    .isEqualTo(ComponentName(context, ControlsFavoritingActivity::class.java))
+                assertThat(getCharSequenceExtra(ControlsFavoritingActivity.EXTRA_STRUCTURE))
+                    .isEqualTo(TEST_STRUCTURE)
+                assertThat(
+                        getParcelableExtra(Intent.EXTRA_COMPONENT_NAME, ComponentName::class.java)
+                    )
+                    .isEqualTo(TEST_COMPONENT)
+                assertThat(getCharSequenceExtra(ControlsFavoritingActivity.EXTRA_APP))
+                    .isEqualTo(TEST_APP)
+                assertThat(getByteExtra(ControlsFavoritingActivity.EXTRA_SOURCE, -1))
+                    .isEqualTo(ControlsFavoritingActivity.EXTRA_SOURCE_VALUE_FROM_EDITING)
+            }
+        }
+    }
+
+    private fun launchActivity(
+        componentName: ComponentName = TEST_COMPONENT,
+        structure: CharSequence = TEST_STRUCTURE,
+        isFromFavoriting: Boolean = false,
+        app: CharSequence = TEST_APP,
+    ): TestableControlsEditingActivity =
+        activityRule.launchActivity(
+            Intent().apply {
+                putExtra(ControlsEditingActivity.EXTRA_FROM_FAVORITING, isFromFavoriting)
+                putExtra(ControlsEditingActivity.EXTRA_STRUCTURE, structure)
+                putExtra(Intent.EXTRA_COMPONENT_NAME, componentName)
+                putExtra(ControlsEditingActivity.EXTRA_APP, app)
+            }
+        )
+
+    class TestableControlsEditingActivity(
+        featureFlags: FakeFeatureFlags,
+        executor: FakeExecutor,
+        controller: ControlsControllerImpl,
+        userTracker: UserTracker,
+        customIconCache: CustomIconCache,
         private val mockDispatcher: OnBackInvokedDispatcher,
         private val latch: CountDownLatch
-    ) : ControlsEditingActivity(executor, controller, userTracker, customIconCache, uiController) {
+    ) :
+        ControlsEditingActivity(
+            featureFlags,
+            executor,
+            controller,
+            userTracker,
+            customIconCache,
+        ) {
+
+        var startActivityData: StartActivityData? = null
+            private set
+
         override fun getOnBackInvokedDispatcher(): OnBackInvokedDispatcher {
             return mockDispatcher
         }
@@ -114,5 +210,13 @@
             // ensures that test runner thread does not proceed until ui thread is done
             latch.countDown()
         }
+
+        override fun startActivity(intent: Intent) {
+            startActivityData = StartActivityData(intent, null)
+        }
+
+        override fun startActivity(intent: Intent, options: Bundle?) {
+            startActivityData = StartActivityData(intent, options)
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
index 3655232..6884616 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
@@ -1,30 +1,49 @@
 package com.android.systemui.controls.management
 
+import android.content.ComponentName
 import android.content.Intent
+import android.os.Bundle
+import android.service.controls.Control
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper
+import android.view.View
+import android.widget.Button
 import android.window.OnBackInvokedCallback
 import android.window.OnBackInvokedDispatcher
 import androidx.test.filters.FlakyTest
 import androidx.test.filters.SmallTest
 import androidx.test.rule.ActivityTestRule
 import androidx.test.runner.intercepting.SingleActivityFactory
+import com.android.systemui.R
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.ControlStatus
+import com.android.systemui.controls.ControlsServiceInfo
+import com.android.systemui.controls.controller.ControlsController
 import com.android.systemui.controls.controller.ControlsControllerImpl
-import com.android.systemui.controls.ui.ControlsUiController
+import com.android.systemui.controls.controller.createLoadDataObject
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
 import com.google.common.util.concurrent.MoreExecutors
 import java.util.concurrent.CountDownLatch
 import java.util.concurrent.Executor
+import java.util.function.Consumer
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.Answers
 import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers
 import org.mockito.Captor
 import org.mockito.Mock
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 
@@ -32,7 +51,19 @@
 @RunWith(AndroidTestingRunner::class)
 @TestableLooper.RunWithLooper
 class ControlsFavoritingActivityTest : SysuiTestCase() {
+
+    private companion object {
+        val TEST_COMPONENT = ComponentName("TestPackageName", "TestClassName")
+        val TEST_CONTROL =
+            mock(Control::class.java, Answers.RETURNS_MOCKS)!!.apply {
+                whenever(structure).thenReturn(TEST_STRUCTURE)
+            }
+        val TEST_STRUCTURE: CharSequence = "TestStructure"
+        val TEST_APP: CharSequence = "TestApp"
+    }
+
     @Main private val executor: Executor = MoreExecutors.directExecutor()
+    private val featureFlags = FakeFeatureFlags()
 
     @Mock lateinit var controller: ControlsControllerImpl
 
@@ -40,13 +71,15 @@
 
     @Mock lateinit var userTracker: UserTracker
 
-    @Mock lateinit var uiController: ControlsUiController
-
-    private lateinit var controlsFavoritingActivity: ControlsFavoritingActivity_Factory
     private var latch: CountDownLatch = CountDownLatch(1)
 
     @Mock private lateinit var mockDispatcher: OnBackInvokedDispatcher
     @Captor private lateinit var captureCallback: ArgumentCaptor<OnBackInvokedCallback>
+    @Captor
+    private lateinit var listingCallback:
+        ArgumentCaptor<ControlsListingController.ControlsListingCallback>
+    @Captor
+    private lateinit var controlsCallback: ArgumentCaptor<Consumer<ControlsController.LoadData>>
 
     @Rule
     @JvmField
@@ -58,11 +91,11 @@
                 ) {
                 override fun create(intent: Intent?): TestableControlsFavoritingActivity {
                     return TestableControlsFavoritingActivity(
+                        featureFlags,
                         executor,
                         controller,
                         listingController,
                         userTracker,
-                        uiController,
                         mockDispatcher,
                         latch
                     )
@@ -75,19 +108,18 @@
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
-        val intent = Intent()
-        intent.putExtra(ControlsFavoritingActivity.EXTRA_FROM_PROVIDER_SELECTOR, true)
-        activityRule.launchActivity(intent)
+        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, false)
     }
 
     // b/259549854 to root-cause and fix
     @FlakyTest
     @Test
     fun testBackCallbackRegistrationAndUnregistration() {
+        launchActivity()
         // 1. ensure that launching the activity results in it registering a callback
         verify(mockDispatcher)
             .registerOnBackInvokedCallback(
-                ArgumentMatchers.eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
+                eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
                 captureCallback.capture()
             )
         activityRule.finishActivity()
@@ -96,22 +128,116 @@
         verify(mockDispatcher).unregisterOnBackInvokedCallback(captureCallback.value)
     }
 
-    public class TestableControlsFavoritingActivity(
+    @Test
+    fun testNewFlowEnabled_buttons() {
+        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
+        with(launchActivity()) {
+            verify(listingController).addCallback(listingCallback.capture())
+            listingCallback.value.onServicesUpdated(
+                listOf(mock(ControlsServiceInfo::class.java), mock(ControlsServiceInfo::class.java))
+            )
+
+            val rearrangeButton = requireViewById<Button>(R.id.rearrange)
+            assertThat(rearrangeButton.visibility).isEqualTo(View.VISIBLE)
+            assertThat(rearrangeButton.isEnabled).isFalse()
+            assertThat(requireViewById<Button>(R.id.other_apps).visibility).isEqualTo(View.GONE)
+        }
+    }
+
+    @Test
+    fun testNewFlowDisabled_buttons() {
+        with(launchActivity()) {
+            verify(listingController).addCallback(listingCallback.capture())
+            activityRule.runOnUiThread {
+                listingCallback.value.onServicesUpdated(
+                    listOf(
+                        mock(ControlsServiceInfo::class.java),
+                        mock(ControlsServiceInfo::class.java)
+                    )
+                )
+            }
+
+            val rearrangeButton = requireViewById<Button>(R.id.rearrange)
+            assertThat(rearrangeButton.visibility).isEqualTo(View.GONE)
+            assertThat(rearrangeButton.isEnabled).isFalse()
+            assertThat(requireViewById<Button>(R.id.other_apps).visibility).isEqualTo(View.VISIBLE)
+        }
+    }
+
+    @Test
+    fun testNewFlowEnabled_rearrangePressed_savesAndlaunchesActivity() {
+        featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
+        with(launchActivity()) {
+            verify(listingController).addCallback(capture(listingCallback))
+            listingCallback.value.onServicesUpdated(
+                listOf(mock(ControlsServiceInfo::class.java), mock(ControlsServiceInfo::class.java))
+            )
+            verify(controller).loadForComponent(any(), capture(controlsCallback), any())
+            activityRule.runOnUiThread {
+                controlsCallback.value.accept(
+                    createLoadDataObject(
+                        listOf(ControlStatus(TEST_CONTROL, TEST_COMPONENT, true)),
+                        emptyList(),
+                    )
+                )
+                requireViewById<Button>(R.id.rearrange).performClick()
+            }
+
+            verify(controller).replaceFavoritesForStructure(any())
+            with(startActivityData!!.intent) {
+                assertThat(component)
+                    .isEqualTo(ComponentName(context, ControlsEditingActivity::class.java))
+                assertThat(
+                        getParcelableExtra(Intent.EXTRA_COMPONENT_NAME, ComponentName::class.java)
+                    )
+                    .isEqualTo(TEST_COMPONENT)
+                assertThat(getCharSequenceExtra(ControlsEditingActivity.EXTRA_APP))
+                    .isEqualTo(TEST_APP)
+                assertThat(getBooleanExtra(ControlsEditingActivity.EXTRA_FROM_FAVORITING, false))
+                    .isTrue()
+                assertThat(getCharSequenceExtra(ControlsEditingActivity.EXTRA_STRUCTURE))
+                    .isEqualTo("")
+            }
+        }
+    }
+
+    private fun launchActivity(
+        componentName: ComponentName = TEST_COMPONENT,
+        structure: CharSequence = TEST_STRUCTURE,
+        app: CharSequence = TEST_APP,
+        source: Byte = ControlsFavoritingActivity.EXTRA_SOURCE_VALUE_FROM_PROVIDER_SELECTOR,
+    ): TestableControlsFavoritingActivity =
+        activityRule.launchActivity(
+            Intent().apply {
+                putExtra(Intent.EXTRA_COMPONENT_NAME, componentName)
+                putExtra(ControlsFavoritingActivity.EXTRA_STRUCTURE, structure)
+                putExtra(ControlsFavoritingActivity.EXTRA_APP, app)
+                putExtra(ControlsFavoritingActivity.EXTRA_SOURCE, source)
+            }
+        )
+
+    class TestableControlsFavoritingActivity(
+        featureFlags: FeatureFlags,
         executor: Executor,
         controller: ControlsControllerImpl,
         listingController: ControlsListingController,
         userTracker: UserTracker,
-        uiController: ControlsUiController,
         private val mockDispatcher: OnBackInvokedDispatcher,
         private val latch: CountDownLatch
     ) :
         ControlsFavoritingActivity(
+            featureFlags,
             executor,
             controller,
             listingController,
             userTracker,
-            uiController
         ) {
+
+        var triedToFinish = false
+
+        var startActivityData: StartActivityData? = null
+            private set
+
         override fun getOnBackInvokedDispatcher(): OnBackInvokedDispatcher {
             return mockDispatcher
         }
@@ -121,5 +247,17 @@
             // ensures that test runner thread does not proceed until ui thread is done
             latch.countDown()
         }
+
+        override fun startActivity(intent: Intent) {
+            startActivityData = StartActivityData(intent, null)
+        }
+
+        override fun startActivity(intent: Intent, options: Bundle?) {
+            startActivityData = StartActivityData(intent, options)
+        }
+
+        override fun animateExitAndFinish() {
+            triedToFinish = true
+        }
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/StartActivityData.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/StartActivityData.kt
new file mode 100644
index 0000000..977e3ba
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/StartActivityData.kt
@@ -0,0 +1,6 @@
+package com.android.systemui.controls.management
+
+import android.content.Intent
+import android.os.Bundle
+
+data class StartActivityData(val intent: Intent, val options: Bundle?)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
index 7f6e2ba..08427da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
@@ -399,7 +399,21 @@
     }
 
     @Test
-    public void testPause() {
+    public void testPauseWithNoActiveSessions() {
+        final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+
+        final Environment environment = new Environment(Stream.of(touchHandler)
+                .collect(Collectors.toCollection(HashSet::new)));
+
+        environment.updateLifecycle(observerOwnerPair -> {
+            observerOwnerPair.first.onPause(observerOwnerPair.second);
+        });
+
+        environment.verifyInputSessionDispose();
+    }
+
+    @Test
+    public void testDeferredPauseWithActiveSessions() {
         final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
 
         final Environment environment = new Environment(Stream.of(touchHandler)
@@ -417,14 +431,59 @@
         environment.publishInputEvent(event);
         verify(eventListener).onInputEvent(eq(event));
 
+        final ArgumentCaptor<DreamTouchHandler.TouchSession> touchSessionArgumentCaptor =
+                ArgumentCaptor.forClass(DreamTouchHandler.TouchSession.class);
+
+        verify(touchHandler).onSessionStart(touchSessionArgumentCaptor.capture());
+
         environment.updateLifecycle(observerOwnerPair -> {
             observerOwnerPair.first.onPause(observerOwnerPair.second);
         });
 
+        verify(environment.mInputSession, never()).dispose();
+
+        // End session
+        touchSessionArgumentCaptor.getValue().pop();
+        environment.executeAll();
+
+        // Check to make sure the input session is now disposed.
         environment.verifyInputSessionDispose();
     }
 
     @Test
+    public void testDestroyWithActiveSessions() {
+        final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+
+        final Environment environment = new Environment(Stream.of(touchHandler)
+                .collect(Collectors.toCollection(HashSet::new)));
+
+        final InputEvent initialEvent = Mockito.mock(InputEvent.class);
+        environment.publishInputEvent(initialEvent);
+
+        // Ensure session started
+        final InputChannelCompat.InputEventListener eventListener =
+                registerInputEventListener(touchHandler);
+
+        // First event will be missed since we register after the execution loop,
+        final InputEvent event = Mockito.mock(InputEvent.class);
+        environment.publishInputEvent(event);
+        verify(eventListener).onInputEvent(eq(event));
+
+        final ArgumentCaptor<DreamTouchHandler.TouchSession> touchSessionArgumentCaptor =
+                ArgumentCaptor.forClass(DreamTouchHandler.TouchSession.class);
+
+        verify(touchHandler).onSessionStart(touchSessionArgumentCaptor.capture());
+
+        environment.updateLifecycle(observerOwnerPair -> {
+            observerOwnerPair.first.onDestroy(observerOwnerPair.second);
+        });
+
+        // Check to make sure the input session is now disposed.
+        environment.verifyInputSessionDispose();
+    }
+
+
+    @Test
     public void testPilfering() {
         final DreamTouchHandler touchHandler1 = Mockito.mock(DreamTouchHandler.class);
         final DreamTouchHandler touchHandler2 = Mockito.mock(DreamTouchHandler.class);
@@ -476,7 +535,7 @@
         environment.executeAll();
 
         environment.updateLifecycle(observerOwnerPair -> {
-            observerOwnerPair.first.onPause(observerOwnerPair.second);
+            observerOwnerPair.first.onDestroy(observerOwnerPair.second);
         });
 
         environment.executeAll();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
new file mode 100644
index 0000000..5704ef3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.dreams.touch;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shared.system.InputChannelCompat;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Optional;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ShadeTouchHandlerTest extends SysuiTestCase {
+    @Mock
+    CentralSurfaces mCentralSurfaces;
+
+    @Mock
+    NotificationPanelViewController mNotificationPanelViewController;
+
+    @Mock
+    DreamTouchHandler.TouchSession mTouchSession;
+
+    ShadeTouchHandler mTouchHandler;
+
+    private static final int TOUCH_HEIGHT = 20;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mTouchHandler = new ShadeTouchHandler(Optional.of(mCentralSurfaces),
+                TOUCH_HEIGHT);
+        when(mCentralSurfaces.getNotificationPanelViewController())
+                .thenReturn(mNotificationPanelViewController);
+    }
+
+    /**
+     * Verify that touches aren't handled when the bouncer is showing.
+     */
+    @Test
+    public void testInactiveOnBouncer() {
+        when(mCentralSurfaces.isBouncerShowing()).thenReturn(true);
+        mTouchHandler.onSessionStart(mTouchSession);
+        verify(mTouchSession).pop();
+    }
+
+    /**
+     * Make sure {@link ShadeTouchHandler}
+     */
+    @Test
+    public void testTouchPilferingOnScroll() {
+        final MotionEvent motionEvent1 = Mockito.mock(MotionEvent.class);
+        final MotionEvent motionEvent2 = Mockito.mock(MotionEvent.class);
+
+        final ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerArgumentCaptor =
+                ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
+
+        mTouchHandler.onSessionStart(mTouchSession);
+        verify(mTouchSession).registerGestureListener(gestureListenerArgumentCaptor.capture());
+
+        assertThat(gestureListenerArgumentCaptor.getValue()
+                .onScroll(motionEvent1, motionEvent2, 1, 1))
+                .isTrue();
+    }
+
+    /**
+     * Ensure touches are propagated to the {@link NotificationPanelViewController}.
+     */
+    @Test
+    public void testEventPropagation() {
+        final MotionEvent motionEvent = Mockito.mock(MotionEvent.class);
+
+        final ArgumentCaptor<InputChannelCompat.InputEventListener>
+                inputEventListenerArgumentCaptor =
+                    ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class);
+
+        mTouchHandler.onSessionStart(mTouchSession);
+        verify(mTouchSession).registerInputListener(inputEventListenerArgumentCaptor.capture());
+        inputEventListenerArgumentCaptor.getValue().onInputEvent(motionEvent);
+        verify(mNotificationPanelViewController).handleExternalTouch(motionEvent);
+    }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt
new file mode 100644
index 0000000..ccd631e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt
@@ -0,0 +1,346 @@
+package com.android.systemui.graphics
+
+import android.content.res.Resources
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.ImageDecoder
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
+import android.graphics.drawable.VectorDrawable
+import android.net.Uri
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileOutputStream
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@kotlinx.coroutines.ExperimentalCoroutinesApi
+@RunWith(AndroidJUnit4::class)
+class ImageLoaderTest : SysuiTestCase() {
+
+    private val testDispatcher = UnconfinedTestDispatcher()
+    private val testScope = TestScope(testDispatcher)
+    private val imageLoader = ImageLoader(context, testDispatcher)
+
+    private lateinit var imgFile: File
+
+    @Before
+    fun setUp() {
+        val context = context.createPackageContext("com.android.systemui.tests", 0)
+        val bitmap =
+            BitmapFactory.decodeResource(
+                context.resources,
+                com.android.systemui.tests.R.drawable.romainguy_rockaway
+            )
+
+        imgFile = File.createTempFile("image", ".png", context.cacheDir)
+        imgFile.deleteOnExit()
+        bitmap.compress(Bitmap.CompressFormat.PNG, 100, FileOutputStream(imgFile))
+    }
+
+    @After
+    fun tearDown() {
+        imgFile.delete()
+    }
+
+    @Test
+    fun invalidResource_drawable_returnsNull() =
+        testScope.runTest { assertThat(imageLoader.loadDrawable(ImageLoader.Res(-1))).isNull() }
+
+    @Test
+    fun invalidResource_bitmap_returnsNull() =
+        testScope.runTest { assertThat(imageLoader.loadBitmap(ImageLoader.Res(-1))).isNull() }
+
+    @Test
+    fun invalidUri_returnsNull() =
+        testScope.runTest {
+            assertThat(imageLoader.loadBitmap(ImageLoader.Uri("this.is/bogus"))).isNull()
+        }
+
+    @Test
+    fun invalidFile_returnsNull() =
+        testScope.runTest {
+            assertThat(imageLoader.loadBitmap(ImageLoader.File("this is broken!"))).isNull()
+        }
+
+    @Test
+    fun invalidIcon_returnsNull() =
+        testScope.runTest {
+            assertThat(imageLoader.loadDrawable(Icon.createWithFilePath("this is broken"))).isNull()
+        }
+
+    @Test
+    fun invalidIS_returnsNull() =
+        testScope.runTest {
+            assertThat(
+                    imageLoader.loadDrawable(
+                        ImageLoader.InputStream(ByteArrayInputStream(ByteArray(0)))
+                    )
+                )
+                .isNull()
+        }
+
+    @Test
+    fun validBitmapResource_loadDrawable_returnsBitmapDrawable() =
+        testScope.runTest {
+            val context = context.createPackageContext("com.android.systemui.tests", 0)
+            val bitmap =
+                BitmapFactory.decodeResource(
+                    context.resources,
+                    com.android.systemui.tests.R.drawable.romainguy_rockaway
+                )
+            assertThat(bitmap).isNotNull()
+            val loadedDrawable =
+                imageLoader.loadDrawable(
+                    ImageLoader.Res(
+                        com.android.systemui.tests.R.drawable.romainguy_rockaway,
+                        context
+                    )
+                )
+            assertBitmapEqualToDrawable(loadedDrawable, bitmap)
+        }
+
+    @Test
+    fun validBitmapResource_loadBitmap_returnsBitmapDrawable() =
+        testScope.runTest {
+            val bitmap =
+                BitmapFactory.decodeResource(
+                    context.resources,
+                    R.drawable.dessert_zombiegingerbread
+                )
+            val loadedBitmap =
+                imageLoader.loadBitmap(ImageLoader.Res(R.drawable.dessert_zombiegingerbread))
+            assertBitmapEqualToBitmap(loadedBitmap, bitmap)
+        }
+
+    @Test
+    fun validBitmapUri_returnsBitmapDrawable() =
+        testScope.runTest {
+            val bitmap =
+                BitmapFactory.decodeResource(
+                    context.resources,
+                    R.drawable.dessert_zombiegingerbread
+                )
+
+            val uri =
+                "android.resource://${context.packageName}/${R.drawable.dessert_zombiegingerbread}"
+            val loadedBitmap = imageLoader.loadBitmap(ImageLoader.Uri(uri))
+            assertBitmapEqualToBitmap(loadedBitmap, bitmap)
+        }
+
+    @Test
+    fun validBitmapFile_returnsBitmapDrawable() =
+        testScope.runTest {
+            val bitmap = BitmapFactory.decodeFile(imgFile.absolutePath)
+            val loadedBitmap = imageLoader.loadBitmap(ImageLoader.File(imgFile))
+            assertBitmapEqualToBitmap(loadedBitmap, bitmap)
+        }
+
+    @Test
+    fun validInputStream_returnsBitmapDrawable() =
+        testScope.runTest {
+            val bitmap = BitmapFactory.decodeFile(imgFile.absolutePath)
+            val loadedBitmap =
+                imageLoader.loadBitmap(ImageLoader.InputStream(FileInputStream(imgFile)))
+            assertBitmapEqualToBitmap(loadedBitmap, bitmap)
+        }
+
+    @Test
+    fun validBitmapIcon_returnsBitmapDrawable() =
+        testScope.runTest {
+            val bitmap =
+                BitmapFactory.decodeResource(
+                    context.resources,
+                    R.drawable.dessert_zombiegingerbread
+                )
+            val loadedDrawable = imageLoader.loadDrawable(Icon.createWithBitmap(bitmap))
+            assertBitmapEqualToDrawable(loadedDrawable, bitmap)
+        }
+
+    @Test
+    fun validUriIcon_returnsBitmapDrawable() =
+        testScope.runTest {
+            val bitmap =
+                BitmapFactory.decodeResource(
+                    context.resources,
+                    R.drawable.dessert_zombiegingerbread
+                )
+            val uri =
+                "android.resource://${context.packageName}/${R.drawable.dessert_zombiegingerbread}"
+            val loadedDrawable = imageLoader.loadDrawable(Icon.createWithContentUri(Uri.parse(uri)))
+            assertBitmapEqualToDrawable(loadedDrawable, bitmap)
+        }
+
+    @Test
+    fun validDataIcon_returnsBitmapDrawable() =
+        testScope.runTest {
+            val bitmap =
+                BitmapFactory.decodeResource(
+                    context.resources,
+                    R.drawable.dessert_zombiegingerbread
+                )
+            val bos =
+                ByteArrayOutputStream(
+                    bitmap.byteCount * 2
+                ) // Compressed bitmap should be smaller than its source.
+            bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos)
+
+            val array = bos.toByteArray()
+            val loadedDrawable = imageLoader.loadDrawable(Icon.createWithData(array, 0, array.size))
+            assertBitmapEqualToDrawable(loadedDrawable, bitmap)
+        }
+
+    @Test
+    fun validSystemResourceIcon_returnsBitmapDrawable() =
+        testScope.runTest {
+            val bitmap =
+                Resources.getSystem().getDrawable(android.R.drawable.ic_dialog_alert, context.theme)
+            val loadedDrawable =
+                imageLoader.loadDrawable(
+                    Icon.createWithResource("android", android.R.drawable.ic_dialog_alert)
+                )
+            assertBitmapEqualToDrawable(loadedDrawable, (bitmap as BitmapDrawable).bitmap)
+        }
+
+    @Test
+    fun invalidDifferentPackageResourceIcon_returnsNull() =
+        testScope.runTest {
+            val loadedDrawable =
+                imageLoader.loadDrawable(
+                    Icon.createWithResource(
+                        "noooope.wrong.package",
+                        R.drawable.dessert_zombiegingerbread
+                    )
+                )
+            assertThat(loadedDrawable).isNull()
+        }
+
+    @Test
+    fun validBitmapResource_widthMoreRestricted_downsizesKeepingAspectRatio() =
+        testScope.runTest {
+            val loadedDrawable =
+                imageLoader.loadDrawable(ImageLoader.File(imgFile), maxWidth = 160, maxHeight = 160)
+            val loadedBitmap = assertBitmapInDrawable(loadedDrawable)
+            assertThat(loadedBitmap.width).isEqualTo(160)
+            assertThat(loadedBitmap.height).isEqualTo(106)
+        }
+
+    @Test
+    fun validBitmapResource_heightMoreRestricted_downsizesKeepingAspectRatio() =
+        testScope.runTest {
+            val loadedDrawable =
+                imageLoader.loadDrawable(ImageLoader.File(imgFile), maxWidth = 160, maxHeight = 50)
+            val loadedBitmap = assertBitmapInDrawable(loadedDrawable)
+            assertThat(loadedBitmap.width).isEqualTo(74)
+            assertThat(loadedBitmap.height).isEqualTo(50)
+        }
+
+    @Test
+    fun validBitmapResource_onlyWidthRestricted_downsizesKeepingAspectRatio() =
+        testScope.runTest {
+            val loadedDrawable =
+                imageLoader.loadDrawable(
+                    ImageLoader.File(imgFile),
+                    maxWidth = 160,
+                    maxHeight = ImageLoader.DO_NOT_RESIZE
+                )
+            val loadedBitmap = assertBitmapInDrawable(loadedDrawable)
+            assertThat(loadedBitmap.width).isEqualTo(160)
+            assertThat(loadedBitmap.height).isEqualTo(106)
+        }
+
+    @Test
+    fun validBitmapResource_onlyHeightRestricted_downsizesKeepingAspectRatio() =
+        testScope.runTest {
+            val loadedDrawable =
+                imageLoader.loadDrawable(
+                    ImageLoader.Res(R.drawable.bubble_thumbnail),
+                    maxWidth = ImageLoader.DO_NOT_RESIZE,
+                    maxHeight = 120
+                )
+            val loadedBitmap = assertBitmapInDrawable(loadedDrawable)
+            assertThat(loadedBitmap.width).isEqualTo(123)
+            assertThat(loadedBitmap.height).isEqualTo(120)
+        }
+
+    @Test
+    fun validVectorDrawable_loadDrawable_successfullyLoaded() =
+        testScope.runTest {
+            val loadedDrawable = imageLoader.loadDrawable(ImageLoader.Res(R.drawable.ic_settings))
+            assertThat(loadedDrawable).isNotNull()
+            assertThat(loadedDrawable).isInstanceOf(VectorDrawable::class.java)
+        }
+
+    @Test
+    fun validVectorDrawable_loadBitmap_returnsNull() =
+        testScope.runTest {
+            val loadedBitmap = imageLoader.loadBitmap(ImageLoader.Res(R.drawable.ic_settings))
+            assertThat(loadedBitmap).isNull()
+        }
+
+    @Test
+    fun validVectorDrawableIcon_loadDrawable_successfullyLoaded() =
+        testScope.runTest {
+            val loadedDrawable =
+                imageLoader.loadDrawable(Icon.createWithResource(context, R.drawable.ic_settings))
+            assertThat(loadedDrawable).isNotNull()
+            assertThat(loadedDrawable).isInstanceOf(VectorDrawable::class.java)
+        }
+
+    @Test
+    fun hardwareAllocator_returnsHardwareBitmap() =
+        testScope.runTest {
+            val loadedDrawable =
+                imageLoader.loadDrawable(
+                    ImageLoader.File(imgFile),
+                    allocator = ImageDecoder.ALLOCATOR_HARDWARE
+                )
+            assertThat(loadedDrawable).isNotNull()
+            assertThat((loadedDrawable as BitmapDrawable).bitmap.config)
+                .isEqualTo(Bitmap.Config.HARDWARE)
+        }
+
+    @Test
+    fun softwareAllocator_returnsSoftwareBitmap() =
+        testScope.runTest {
+            val loadedDrawable =
+                imageLoader.loadDrawable(
+                    ImageLoader.File(imgFile),
+                    allocator = ImageDecoder.ALLOCATOR_SOFTWARE
+                )
+            assertThat(loadedDrawable).isNotNull()
+            assertThat((loadedDrawable as BitmapDrawable).bitmap.config)
+                .isNotEqualTo(Bitmap.Config.HARDWARE)
+        }
+
+    private fun assertBitmapInDrawable(drawable: Drawable?): Bitmap {
+        assertThat(drawable).isNotNull()
+        assertThat(drawable).isInstanceOf(BitmapDrawable::class.java)
+        return (drawable as BitmapDrawable).bitmap
+    }
+
+    private fun assertBitmapEqualToDrawable(actual: Drawable?, expected: Bitmap) {
+        val actualBitmap = assertBitmapInDrawable(actual)
+        assertBitmapEqualToBitmap(actualBitmap, expected)
+    }
+
+    private fun assertBitmapEqualToBitmap(actual: Bitmap?, expected: Bitmap) {
+        assertThat(actual).isNotNull()
+        assertThat(actual?.width).isEqualTo(expected.width)
+        assertThat(actual?.height).isEqualTo(expected.height)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
index fb7d379..5d83f56 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt
@@ -157,11 +157,34 @@
             assertThat(strongBiometricAllowed()).isFalse()
         }
 
+    @Test
+    fun convenienceBiometricAllowedChange() =
+        testScope.runTest {
+            createBiometricSettingsRepository()
+            val convenienceBiometricAllowed =
+                collectLastValue(underTest.isNonStrongBiometricAllowed)
+            runCurrent()
+
+            onNonStrongAuthChanged(true, PRIMARY_USER_ID)
+            assertThat(convenienceBiometricAllowed()).isTrue()
+
+            onNonStrongAuthChanged(false, ANOTHER_USER_ID)
+            assertThat(convenienceBiometricAllowed()).isTrue()
+
+            onNonStrongAuthChanged(false, PRIMARY_USER_ID)
+            assertThat(convenienceBiometricAllowed()).isFalse()
+        }
+
     private fun onStrongAuthChanged(flags: Int, userId: Int) {
         strongAuthTracker.value.stub.onStrongAuthRequiredChanged(flags, userId)
         testableLooper?.processAllMessages() // StrongAuthTracker uses the TestableLooper
     }
 
+    private fun onNonStrongAuthChanged(allowed: Boolean, userId: Int) {
+        strongAuthTracker.value.stub.onIsNonStrongBiometricAllowedChanged(allowed, userId)
+        testableLooper?.processAllMessages() // StrongAuthTracker uses the TestableLooper
+    }
+
     @Test
     fun fingerprintDisabledByDpmChange() =
         testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
new file mode 100644
index 0000000..6e002f5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -0,0 +1,920 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.app.StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
+import android.app.StatusBarManager.SESSION_KEYGUARD
+import android.content.pm.UserInfo
+import android.content.pm.UserInfo.FLAG_PRIMARY
+import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_CANCELED
+import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT
+import android.hardware.biometrics.ComponentInfoInternal
+import android.hardware.face.FaceAuthenticateOptions
+import android.hardware.face.FaceManager
+import android.hardware.face.FaceSensorProperties
+import android.hardware.face.FaceSensorPropertiesInternal
+import android.os.CancellationSignal
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.InstanceId.fakeInstanceId
+import com.android.internal.logging.UiEventLogger
+import com.android.keyguard.FaceAuthUiEvent
+import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
+import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.FlowValue
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
+import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.shared.model.AuthenticationStatus
+import com.android.systemui.keyguard.shared.model.DetectionStatus
+import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.WakeSleepReason
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
+import com.android.systemui.log.FaceAuthenticationLogger
+import com.android.systemui.log.SessionTracker
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.phone.FakeKeyguardStateController
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.mockito.KotlinArgumentCaptor
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.SystemClock
+import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestDispatcher
+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.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.isNull
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
+    private lateinit var underTest: DeviceEntryFaceAuthRepositoryImpl
+
+    @Mock private lateinit var faceManager: FaceManager
+    @Mock private lateinit var bypassController: KeyguardBypassController
+    @Mock private lateinit var sessionTracker: SessionTracker
+    @Mock private lateinit var uiEventLogger: UiEventLogger
+    @Mock private lateinit var dumpManager: DumpManager
+
+    @Captor
+    private lateinit var authenticationCallback: ArgumentCaptor<FaceManager.AuthenticationCallback>
+
+    @Captor
+    private lateinit var detectionCallback: ArgumentCaptor<FaceManager.FaceDetectionCallback>
+    @Captor private lateinit var cancellationSignal: ArgumentCaptor<CancellationSignal>
+
+    private lateinit var bypassStateChangedListener:
+        KotlinArgumentCaptor<KeyguardBypassController.OnBypassStateChangedListener>
+
+    @Captor
+    private lateinit var faceLockoutResetCallback: ArgumentCaptor<FaceManager.LockoutResetCallback>
+    private lateinit var testDispatcher: TestDispatcher
+
+    private lateinit var testScope: TestScope
+    private lateinit var fakeUserRepository: FakeUserRepository
+    private lateinit var authStatus: FlowValue<AuthenticationStatus?>
+    private lateinit var detectStatus: FlowValue<DetectionStatus?>
+    private lateinit var authRunning: FlowValue<Boolean?>
+    private lateinit var lockedOut: FlowValue<Boolean?>
+    private lateinit var canFaceAuthRun: FlowValue<Boolean?>
+    private lateinit var authenticated: FlowValue<Boolean?>
+    private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
+    private lateinit var deviceEntryFingerprintAuthRepository:
+        FakeDeviceEntryFingerprintAuthRepository
+    private lateinit var trustRepository: FakeTrustRepository
+    private lateinit var keyguardRepository: FakeKeyguardRepository
+    private lateinit var keyguardInteractor: KeyguardInteractor
+    private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
+    private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
+    private lateinit var fakeCommandQueue: FakeCommandQueue
+    private lateinit var featureFlags: FakeFeatureFlags
+
+    private var wasAuthCancelled = false
+    private var wasDetectCancelled = false
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+        fakeUserRepository = FakeUserRepository()
+        fakeUserRepository.setUserInfos(listOf(primaryUser, secondaryUser))
+        testDispatcher = StandardTestDispatcher()
+        biometricSettingsRepository = FakeBiometricSettingsRepository()
+        deviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
+        trustRepository = FakeTrustRepository()
+        keyguardRepository = FakeKeyguardRepository()
+        bouncerRepository = FakeKeyguardBouncerRepository()
+        featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) }
+        fakeCommandQueue = FakeCommandQueue()
+        keyguardInteractor =
+            KeyguardInteractor(
+                keyguardRepository,
+                fakeCommandQueue,
+                featureFlags,
+                bouncerRepository
+            )
+        alternateBouncerInteractor =
+            AlternateBouncerInteractor(
+                bouncerRepository = bouncerRepository,
+                biometricSettingsRepository = biometricSettingsRepository,
+                deviceEntryFingerprintAuthRepository = deviceEntryFingerprintAuthRepository,
+                systemClock = mock(SystemClock::class.java),
+                keyguardStateController = FakeKeyguardStateController(),
+                statusBarStateController = mock(StatusBarStateController::class.java),
+            )
+
+        bypassStateChangedListener =
+            KotlinArgumentCaptor(KeyguardBypassController.OnBypassStateChangedListener::class.java)
+        testScope = TestScope(testDispatcher)
+        whenever(sessionTracker.getSessionId(SESSION_KEYGUARD)).thenReturn(keyguardSessionId)
+        whenever(faceManager.sensorPropertiesInternal)
+            .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = true)))
+        whenever(bypassController.bypassEnabled).thenReturn(true)
+        underTest = createDeviceEntryFaceAuthRepositoryImpl(faceManager, bypassController)
+    }
+
+    private fun createDeviceEntryFaceAuthRepositoryImpl(
+        fmOverride: FaceManager? = faceManager,
+        bypassControllerOverride: KeyguardBypassController? = bypassController
+    ) =
+        DeviceEntryFaceAuthRepositoryImpl(
+            mContext,
+            fmOverride,
+            fakeUserRepository,
+            bypassControllerOverride,
+            testScope.backgroundScope,
+            testDispatcher,
+            sessionTracker,
+            uiEventLogger,
+            FaceAuthenticationLogger(logcatLogBuffer("DeviceEntryFaceAuthRepositoryLog")),
+            biometricSettingsRepository,
+            deviceEntryFingerprintAuthRepository,
+            trustRepository,
+            keyguardRepository,
+            keyguardInteractor,
+            alternateBouncerInteractor,
+            dumpManager,
+        )
+
+    @Test
+    fun faceAuthRunsAndProvidesAuthStatusUpdates() =
+        testScope.runTest {
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
+
+            FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER.extraInfo = 10
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+            uiEventIsLogged(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+
+            assertThat(authRunning()).isTrue()
+
+            val successResult = successResult()
+            authenticationCallback.value.onAuthenticationSucceeded(successResult)
+
+            assertThat(authStatus()).isEqualTo(SuccessAuthenticationStatus(successResult))
+            assertThat(authenticated()).isTrue()
+            assertThat(authRunning()).isFalse()
+        }
+
+    private fun uiEventIsLogged(faceAuthUiEvent: FaceAuthUiEvent) {
+        verify(uiEventLogger)
+            .logWithInstanceIdAndPosition(
+                faceAuthUiEvent,
+                0,
+                null,
+                keyguardSessionId,
+                faceAuthUiEvent.extraInfo
+            )
+    }
+
+    @Test
+    fun faceAuthDoesNotRunWhileItIsAlreadyRunning() =
+        testScope.runTest {
+            initCollectors()
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+            clearInvocations(faceManager)
+            clearInvocations(uiEventLogger)
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            verifyNoMoreInteractions(faceManager)
+            verifyNoMoreInteractions(uiEventLogger)
+        }
+
+    @Test
+    fun faceLockoutStatusIsPropagated() =
+        testScope.runTest {
+            initCollectors()
+            verify(faceManager).addLockoutResetCallback(faceLockoutResetCallback.capture())
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+
+            authenticationCallback.value.onAuthenticationError(
+                FACE_ERROR_LOCKOUT_PERMANENT,
+                "face locked out"
+            )
+
+            assertThat(lockedOut()).isTrue()
+
+            faceLockoutResetCallback.value.onLockoutReset(0)
+            assertThat(lockedOut()).isFalse()
+        }
+
+    @Test
+    fun faceDetectionSupportIsTheCorrectValue() =
+        testScope.runTest {
+            assertThat(
+                    createDeviceEntryFaceAuthRepositoryImpl(fmOverride = null).isDetectionSupported
+                )
+                .isFalse()
+
+            whenever(faceManager.sensorPropertiesInternal).thenReturn(null)
+            assertThat(createDeviceEntryFaceAuthRepositoryImpl().isDetectionSupported).isFalse()
+
+            whenever(faceManager.sensorPropertiesInternal).thenReturn(listOf())
+            assertThat(createDeviceEntryFaceAuthRepositoryImpl().isDetectionSupported).isFalse()
+
+            whenever(faceManager.sensorPropertiesInternal)
+                .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = false)))
+            assertThat(createDeviceEntryFaceAuthRepositoryImpl().isDetectionSupported).isFalse()
+
+            whenever(faceManager.sensorPropertiesInternal)
+                .thenReturn(
+                    listOf(
+                        createFaceSensorProperties(supportsFaceDetection = false),
+                        createFaceSensorProperties(supportsFaceDetection = true)
+                    )
+                )
+            assertThat(createDeviceEntryFaceAuthRepositoryImpl().isDetectionSupported).isFalse()
+
+            whenever(faceManager.sensorPropertiesInternal)
+                .thenReturn(
+                    listOf(
+                        createFaceSensorProperties(supportsFaceDetection = true),
+                        createFaceSensorProperties(supportsFaceDetection = false)
+                    )
+                )
+            assertThat(createDeviceEntryFaceAuthRepositoryImpl().isDetectionSupported).isTrue()
+        }
+
+    @Test
+    fun cancelStopsFaceAuthentication() =
+        testScope.runTest {
+            initCollectors()
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+
+            var wasAuthCancelled = false
+            cancellationSignal.value.setOnCancelListener { wasAuthCancelled = true }
+
+            underTest.cancel()
+            assertThat(wasAuthCancelled).isTrue()
+            assertThat(authRunning()).isFalse()
+        }
+
+    @Test
+    fun cancelInvokedWithoutFaceAuthRunningIsANoop() = testScope.runTest { underTest.cancel() }
+
+    @Test
+    fun faceDetectionRunsAndPropagatesDetectionStatus() =
+        testScope.runTest {
+            whenever(faceManager.sensorPropertiesInternal)
+                .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = true)))
+            underTest = createDeviceEntryFaceAuthRepositoryImpl()
+            initCollectors()
+
+            underTest.detect()
+            faceDetectIsCalled()
+
+            detectionCallback.value.onFaceDetected(1, 1, true)
+
+            assertThat(detectStatus()).isEqualTo(DetectionStatus(1, 1, true))
+        }
+
+    @Test
+    fun faceDetectDoesNotRunIfDetectionIsNotSupported() =
+        testScope.runTest {
+            whenever(faceManager.sensorPropertiesInternal)
+                .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = false)))
+            underTest = createDeviceEntryFaceAuthRepositoryImpl()
+            initCollectors()
+            clearInvocations(faceManager)
+
+            underTest.detect()
+
+            verify(faceManager, never())
+                .detectFace(any(), any(), any(FaceAuthenticateOptions::class.java))
+        }
+
+    @Test
+    fun faceAuthShouldWaitAndRunIfTriggeredWhileCancelling() =
+        testScope.runTest {
+            initCollectors()
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+
+            // Enter cancelling state
+            underTest.cancel()
+            clearInvocations(faceManager)
+
+            // Auth is while cancelling.
+            underTest.authenticate(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
+            // Auth is not started
+            verifyNoMoreInteractions(faceManager)
+
+            // Auth is done cancelling.
+            authenticationCallback.value.onAuthenticationError(
+                FACE_ERROR_CANCELED,
+                "First auth attempt cancellation completed"
+            )
+            assertThat(authStatus())
+                .isEqualTo(
+                    ErrorAuthenticationStatus(
+                        FACE_ERROR_CANCELED,
+                        "First auth attempt cancellation completed"
+                    )
+                )
+
+            faceAuthenticateIsCalled()
+            uiEventIsLogged(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
+        }
+
+    @Test
+    fun faceAuthAutoCancelsAfterDefaultCancellationTimeout() =
+        testScope.runTest {
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+
+            clearInvocations(faceManager)
+            underTest.cancel()
+            advanceTimeBy(DeviceEntryFaceAuthRepositoryImpl.DEFAULT_CANCEL_SIGNAL_TIMEOUT + 1)
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+        }
+
+    @Test
+    fun faceHelpMessagesAreIgnoredBasedOnConfig() =
+        testScope.runTest {
+            overrideResource(
+                R.array.config_face_acquire_device_entry_ignorelist,
+                intArrayOf(10, 11)
+            )
+            underTest = createDeviceEntryFaceAuthRepositoryImpl()
+            initCollectors()
+
+            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+            faceAuthenticateIsCalled()
+
+            authenticationCallback.value.onAuthenticationHelp(9, "help msg")
+            authenticationCallback.value.onAuthenticationHelp(10, "Ignored help msg")
+            authenticationCallback.value.onAuthenticationHelp(11, "Ignored help msg")
+
+            assertThat(authStatus()).isEqualTo(HelpAuthenticationStatus(9, "help msg"))
+        }
+
+    @Test
+    fun dumpDoesNotErrorOutWhenFaceManagerOrBypassControllerIsNull() =
+        testScope.runTest {
+            fakeUserRepository.setSelectedUserInfo(primaryUser)
+            underTest.dump(PrintWriter(StringWriter()), emptyArray())
+
+            underTest =
+                createDeviceEntryFaceAuthRepositoryImpl(
+                    fmOverride = null,
+                    bypassControllerOverride = null
+                )
+            fakeUserRepository.setSelectedUserInfo(primaryUser)
+
+            underTest.dump(PrintWriter(StringWriter()), emptyArray())
+        }
+
+    @Test
+    fun authenticateDoesNotRunIfFaceIsNotEnrolled() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth { biometricSettingsRepository.setFaceEnrolled(false) }
+        }
+
+    @Test
+    fun authenticateDoesNotRunIfFaceIsNotEnabled() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth { biometricSettingsRepository.setIsFaceAuthEnabled(false) }
+        }
+
+    @Test
+    fun authenticateDoesNotRunIfUserIsInLockdown() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth { biometricSettingsRepository.setIsUserInLockdown(true) }
+        }
+
+    @Test
+    fun authenticateDoesNotRunIfUserIsCurrentlySwitching() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth { fakeUserRepository.setUserSwitching(true) }
+        }
+
+    @Test
+    fun authenticateDoesNotRunWhenFpIsLockedOut() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth { deviceEntryFingerprintAuthRepository.setLockedOut(true) }
+        }
+
+    @Test
+    fun authenticateDoesNotRunWhenUserIsCurrentlyTrusted() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth { trustRepository.setCurrentUserTrusted(true) }
+        }
+
+    @Test
+    fun authenticateDoesNotRunWhenKeyguardIsGoingAway() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth { keyguardRepository.setKeyguardGoingAway(true) }
+        }
+
+    @Test
+    fun authenticateDoesNotRunWhenDeviceIsGoingToSleep() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth {
+                keyguardRepository.setWakefulnessModel(
+                    WakefulnessModel(
+                        state = WakefulnessState.STARTING_TO_SLEEP,
+                        isWakingUpOrAwake = false,
+                        lastWakeReason = WakeSleepReason.OTHER,
+                        lastSleepReason = WakeSleepReason.OTHER,
+                    )
+                )
+            }
+        }
+
+    @Test
+    fun authenticateDoesNotRunWhenDeviceIsSleeping() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth {
+                keyguardRepository.setWakefulnessModel(
+                    WakefulnessModel(
+                        state = WakefulnessState.ASLEEP,
+                        isWakingUpOrAwake = false,
+                        lastWakeReason = WakeSleepReason.OTHER,
+                        lastSleepReason = WakeSleepReason.OTHER,
+                    )
+                )
+            }
+        }
+
+    @Test
+    fun authenticateDoesNotRunWhenNonStrongBiometricIsNotAllowed() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth {
+                biometricSettingsRepository.setIsNonStrongBiometricAllowed(false)
+            }
+        }
+
+    @Test
+    fun authenticateDoesNotRunWhenCurrentUserIsNotPrimary() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth {
+                launch { fakeUserRepository.setSelectedUserInfo(secondaryUser) }
+            }
+        }
+
+    @Test
+    fun authenticateDoesNotRunWhenSecureCameraIsActive() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth {
+                bouncerRepository.setAlternateVisible(false)
+                fakeCommandQueue.doForEachCallback {
+                    it.onCameraLaunchGestureDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
+                }
+            }
+        }
+
+    @Test
+    fun authenticateDoesNotRunOnUnsupportedPosture() =
+        testScope.runTest {
+            testGatingCheckForFaceAuth {
+                biometricSettingsRepository.setIsFaceAuthSupportedInCurrentPosture(false)
+            }
+        }
+
+    @Test
+    fun authenticateFallbacksToDetectionWhenItCannotRun() =
+        testScope.runTest {
+            whenever(faceManager.sensorPropertiesInternal)
+                .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = true)))
+            whenever(bypassController.bypassEnabled).thenReturn(true)
+            underTest = createDeviceEntryFaceAuthRepositoryImpl()
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
+
+            // Flip one precondition to false.
+            biometricSettingsRepository.setIsNonStrongBiometricAllowed(false)
+            assertThat(canFaceAuthRun()).isFalse()
+            underTest.authenticate(
+                FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
+                fallbackToDetection = true
+            )
+            faceAuthenticateIsNotCalled()
+
+            faceDetectIsCalled()
+        }
+
+    @Test
+    fun everythingWorksWithFaceAuthRefactorFlagDisabled() =
+        testScope.runTest {
+            featureFlags.set(FACE_AUTH_REFACTOR, false)
+
+            underTest = createDeviceEntryFaceAuthRepositoryImpl()
+            initCollectors()
+
+            // Collecting any flows exposed in the public API doesn't throw any error
+            authStatus()
+            detectStatus()
+            authRunning()
+            lockedOut()
+            canFaceAuthRun()
+            authenticated()
+        }
+
+    @Test
+    fun isAuthenticatedIsFalseWhenFaceAuthFails() =
+        testScope.runTest {
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
+
+            triggerFaceAuth(false)
+
+            authenticationCallback.value.onAuthenticationFailed()
+
+            assertThat(authenticated()).isFalse()
+        }
+
+    @Test
+    fun isAuthenticatedIsFalseWhenFaceAuthErrorsOut() =
+        testScope.runTest {
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
+
+            triggerFaceAuth(false)
+
+            authenticationCallback.value.onAuthenticationError(-1, "some error")
+
+            assertThat(authenticated()).isFalse()
+        }
+
+    @Test
+    fun isAuthenticatedIsResetToFalseWhenKeyguardIsGoingAway() =
+        testScope.runTest {
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
+
+            triggerFaceAuth(false)
+
+            authenticationCallback.value.onAuthenticationSucceeded(
+                mock(FaceManager.AuthenticationResult::class.java)
+            )
+
+            assertThat(authenticated()).isTrue()
+
+            keyguardRepository.setKeyguardGoingAway(true)
+
+            assertThat(authenticated()).isFalse()
+        }
+
+    @Test
+    fun isAuthenticatedIsResetToFalseWhenUserIsSwitching() =
+        testScope.runTest {
+            initCollectors()
+            allPreconditionsToRunFaceAuthAreTrue()
+
+            triggerFaceAuth(false)
+
+            authenticationCallback.value.onAuthenticationSucceeded(
+                mock(FaceManager.AuthenticationResult::class.java)
+            )
+
+            assertThat(authenticated()).isTrue()
+
+            fakeUserRepository.setUserSwitching(true)
+
+            assertThat(authenticated()).isFalse()
+        }
+
+    @Test
+    fun detectDoesNotRunWhenFaceIsNotEnrolled() =
+        testScope.runTest {
+            testGatingCheckForDetect { biometricSettingsRepository.setFaceEnrolled(false) }
+        }
+
+    @Test
+    fun detectDoesNotRunWhenFaceIsNotEnabled() =
+        testScope.runTest {
+            testGatingCheckForDetect { biometricSettingsRepository.setIsFaceAuthEnabled(false) }
+        }
+
+    @Test
+    fun detectDoesNotRunWhenUserSwitchingInProgress() =
+        testScope.runTest { testGatingCheckForDetect { fakeUserRepository.setUserSwitching(true) } }
+
+    @Test
+    fun detectDoesNotRunWhenKeyguardGoingAway() =
+        testScope.runTest {
+            testGatingCheckForDetect { keyguardRepository.setKeyguardGoingAway(true) }
+        }
+
+    @Test
+    fun detectDoesNotRunWhenDeviceSleepingStartingToSleep() =
+        testScope.runTest {
+            testGatingCheckForDetect {
+                keyguardRepository.setWakefulnessModel(
+                    WakefulnessModel(
+                        state = WakefulnessState.STARTING_TO_SLEEP,
+                        isWakingUpOrAwake = false,
+                        lastWakeReason = WakeSleepReason.OTHER,
+                        lastSleepReason = WakeSleepReason.OTHER,
+                    )
+                )
+            }
+        }
+
+    @Test
+    fun detectDoesNotRunWhenSecureCameraIsActive() =
+        testScope.runTest {
+            testGatingCheckForDetect {
+                bouncerRepository.setAlternateVisible(false)
+                fakeCommandQueue.doForEachCallback {
+                    it.onCameraLaunchGestureDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
+                }
+            }
+        }
+
+    @Test
+    fun detectDoesNotRunWhenFaceAuthNotSupportedInCurrentPosture() =
+        testScope.runTest {
+            testGatingCheckForDetect {
+                biometricSettingsRepository.setIsFaceAuthSupportedInCurrentPosture(false)
+            }
+        }
+
+    @Test
+    fun detectDoesNotRunWhenCurrentUserInLockdown() =
+        testScope.runTest {
+            testGatingCheckForDetect { biometricSettingsRepository.setIsUserInLockdown(true) }
+        }
+
+    @Test
+    fun detectDoesNotRunWhenBypassIsNotEnabled() =
+        testScope.runTest {
+            runCurrent()
+            verify(bypassController)
+                .registerOnBypassStateChangedListener(bypassStateChangedListener.capture())
+
+            testGatingCheckForDetect {
+                bypassStateChangedListener.value.onBypassStateChanged(false)
+            }
+        }
+
+    @Test
+    fun detectDoesNotRunWhenNonStrongBiometricIsAllowed() =
+        testScope.runTest {
+            testGatingCheckForDetect {
+                biometricSettingsRepository.setIsNonStrongBiometricAllowed(true)
+            }
+        }
+
+    @Test
+    fun detectDoesNotRunIfUdfpsIsRunning() =
+        testScope.runTest {
+            testGatingCheckForDetect {
+                deviceEntryFingerprintAuthRepository.setAvailableFpSensorType(
+                    BiometricType.UNDER_DISPLAY_FINGERPRINT
+                )
+                deviceEntryFingerprintAuthRepository.setIsRunning(true)
+            }
+        }
+
+    private suspend fun TestScope.testGatingCheckForFaceAuth(gatingCheckModifier: () -> Unit) {
+        initCollectors()
+        allPreconditionsToRunFaceAuthAreTrue()
+
+        gatingCheckModifier()
+        runCurrent()
+
+        // gating check doesn't allow face auth to run.
+        assertThat(underTest.canRunFaceAuth.value).isFalse()
+
+        // flip the gating check back on.
+        allPreconditionsToRunFaceAuthAreTrue()
+
+        triggerFaceAuth(false)
+
+        // Flip gating check off
+        gatingCheckModifier()
+        runCurrent()
+
+        // Stops currently running auth
+        assertThat(wasAuthCancelled).isTrue()
+        clearInvocations(faceManager)
+
+        // Try auth again
+        underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+
+        // Auth can't run again
+        faceAuthenticateIsNotCalled()
+    }
+
+    private suspend fun TestScope.testGatingCheckForDetect(gatingCheckModifier: () -> Unit) {
+        initCollectors()
+        allPreconditionsToRunFaceAuthAreTrue()
+
+        // This will stop face auth from running but is required to be false for detect.
+        biometricSettingsRepository.setIsNonStrongBiometricAllowed(false)
+        runCurrent()
+
+        assertThat(canFaceAuthRun()).isFalse()
+
+        // Trigger authenticate with detection fallback
+        underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetection = true)
+
+        faceAuthenticateIsNotCalled()
+        faceDetectIsCalled()
+        cancellationSignal.value.setOnCancelListener { wasDetectCancelled = true }
+
+        // Flip gating check
+        gatingCheckModifier()
+        runCurrent()
+
+        // Stops currently running detect
+        assertThat(wasDetectCancelled).isTrue()
+        clearInvocations(faceManager)
+
+        // Try to run detect again
+        underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetection = true)
+
+        // Detect won't run because preconditions are not true anymore.
+        faceDetectIsNotCalled()
+    }
+
+    private suspend fun triggerFaceAuth(fallbackToDetect: Boolean) {
+        assertThat(canFaceAuthRun()).isTrue()
+        underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetect)
+        faceAuthenticateIsCalled()
+        assertThat(authRunning()).isTrue()
+        cancellationSignal.value.setOnCancelListener { wasAuthCancelled = true }
+    }
+
+    private suspend fun TestScope.allPreconditionsToRunFaceAuthAreTrue() {
+        biometricSettingsRepository.setFaceEnrolled(true)
+        biometricSettingsRepository.setIsFaceAuthEnabled(true)
+        fakeUserRepository.setUserSwitching(false)
+        deviceEntryFingerprintAuthRepository.setLockedOut(false)
+        trustRepository.setCurrentUserTrusted(false)
+        keyguardRepository.setKeyguardGoingAway(false)
+        keyguardRepository.setWakefulnessModel(
+            WakefulnessModel(
+                WakefulnessState.STARTING_TO_WAKE,
+                true,
+                WakeSleepReason.OTHER,
+                WakeSleepReason.OTHER
+            )
+        )
+        biometricSettingsRepository.setIsNonStrongBiometricAllowed(true)
+        biometricSettingsRepository.setIsUserInLockdown(false)
+        fakeUserRepository.setSelectedUserInfo(primaryUser)
+        biometricSettingsRepository.setIsFaceAuthSupportedInCurrentPosture(true)
+        bouncerRepository.setAlternateVisible(true)
+        runCurrent()
+    }
+
+    private suspend fun TestScope.initCollectors() {
+        authStatus = collectLastValue(underTest.authenticationStatus)
+        detectStatus = collectLastValue(underTest.detectionStatus)
+        authRunning = collectLastValue(underTest.isAuthRunning)
+        lockedOut = collectLastValue(underTest.isLockedOut)
+        canFaceAuthRun = collectLastValue(underTest.canRunFaceAuth)
+        authenticated = collectLastValue(underTest.isAuthenticated)
+        fakeUserRepository.setSelectedUserInfo(primaryUser)
+    }
+
+    private fun successResult() = FaceManager.AuthenticationResult(null, null, primaryUserId, false)
+
+    private fun faceDetectIsCalled() {
+        verify(faceManager)
+            .detectFace(
+                cancellationSignal.capture(),
+                detectionCallback.capture(),
+                eq(FaceAuthenticateOptions.Builder().setUserId(primaryUserId).build())
+            )
+    }
+
+    private fun faceAuthenticateIsCalled() {
+        verify(faceManager)
+            .authenticate(
+                isNull(),
+                cancellationSignal.capture(),
+                authenticationCallback.capture(),
+                isNull(),
+                eq(FaceAuthenticateOptions.Builder().setUserId(primaryUserId).build())
+            )
+    }
+
+    private fun faceAuthenticateIsNotCalled() {
+        verify(faceManager, never())
+            .authenticate(
+                isNull(),
+                any(),
+                any(),
+                isNull(),
+                any(FaceAuthenticateOptions::class.java)
+            )
+    }
+
+    private fun faceDetectIsNotCalled() {
+        verify(faceManager, never())
+            .detectFace(any(), any(), any(FaceAuthenticateOptions::class.java))
+    }
+
+    private fun createFaceSensorProperties(
+        supportsFaceDetection: Boolean
+    ): FaceSensorPropertiesInternal {
+        val componentInfo =
+            listOf(
+                ComponentInfoInternal(
+                    "faceSensor" /* componentId */,
+                    "vendor/model/revision" /* hardwareVersion */,
+                    "1.01" /* firmwareVersion */,
+                    "00000001" /* serialNumber */,
+                    "" /* softwareVersion */
+                )
+            )
+        return FaceSensorPropertiesInternal(
+            0 /* id */,
+            FaceSensorProperties.STRENGTH_STRONG,
+            1 /* maxTemplatesAllowed */,
+            componentInfo,
+            FaceSensorProperties.TYPE_UNKNOWN,
+            supportsFaceDetection /* supportsFaceDetection */,
+            true /* supportsSelfIllumination */,
+            false /* resetLockoutRequiresChallenge */
+        )
+    }
+
+    companion object {
+        const val primaryUserId = 1
+        val keyguardSessionId = fakeInstanceId(10)!!
+        val primaryUser = UserInfo(primaryUserId, "test user", FLAG_PRIMARY)
+
+        val secondaryUser = UserInfo(2, "secondary user", 0)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
index 70f766f..e57b044 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.data.repository
 
+import android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT
 import android.hardware.biometrics.BiometricSourceType
 import androidx.test.filters.SmallTest
 import com.android.keyguard.KeyguardUpdateMonitor
@@ -30,7 +31,6 @@
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
-import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -70,11 +70,6 @@
             )
     }
 
-    @After
-    fun tearDown() {
-        //        verify(keyguardUpdateMonitor).removeCallback(updateMonitorCallback.value)
-    }
-
     @Test
     fun isLockedOut_whenFingerprintLockoutStateChanges_emitsNewValue() =
         testScope.runTest {
@@ -129,29 +124,55 @@
     }
 
     @Test
-    fun enabledFingerprintTypeProvidesTheCorrectOutput() =
+    fun enabledFingerprintTypeProvidesTheCorrectOutputForSpfs() =
         testScope.runTest {
             whenever(authController.isSfpsSupported).thenReturn(true)
             whenever(authController.isUdfpsSupported).thenReturn(false)
             whenever(authController.isRearFpsSupported).thenReturn(false)
 
-            assertThat(underTest.availableFpSensorType).isEqualTo(BiometricType.SIDE_FINGERPRINT)
+            val availableFpSensorType = collectLastValue(underTest.availableFpSensorType)
+            assertThat(availableFpSensorType()).isEqualTo(BiometricType.SIDE_FINGERPRINT)
+        }
 
+    @Test
+    fun enabledFingerprintTypeProvidesTheCorrectOutputForUdfps() =
+        testScope.runTest {
             whenever(authController.isSfpsSupported).thenReturn(false)
             whenever(authController.isUdfpsSupported).thenReturn(true)
             whenever(authController.isRearFpsSupported).thenReturn(false)
+            val availableFpSensorType = collectLastValue(underTest.availableFpSensorType)
+            assertThat(availableFpSensorType()).isEqualTo(BiometricType.UNDER_DISPLAY_FINGERPRINT)
+        }
 
-            assertThat(underTest.availableFpSensorType)
-                .isEqualTo(BiometricType.UNDER_DISPLAY_FINGERPRINT)
-
+    @Test
+    fun enabledFingerprintTypeProvidesTheCorrectOutputForRearFps() =
+        testScope.runTest {
             whenever(authController.isSfpsSupported).thenReturn(false)
             whenever(authController.isUdfpsSupported).thenReturn(false)
             whenever(authController.isRearFpsSupported).thenReturn(true)
 
-            assertThat(underTest.availableFpSensorType).isEqualTo(BiometricType.REAR_FINGERPRINT)
+            val availableFpSensorType = collectLastValue(underTest.availableFpSensorType)
 
+            assertThat(availableFpSensorType()).isEqualTo(BiometricType.REAR_FINGERPRINT)
+        }
+
+    @Test
+    fun enabledFingerprintTypeProvidesTheCorrectOutputAfterAllAuthenticatorsAreRegistered() =
+        testScope.runTest {
+            whenever(authController.isSfpsSupported).thenReturn(false)
+            whenever(authController.isUdfpsSupported).thenReturn(false)
             whenever(authController.isRearFpsSupported).thenReturn(false)
+            whenever(authController.areAllFingerprintAuthenticatorsRegistered()).thenReturn(false)
 
-            assertThat(underTest.availableFpSensorType).isNull()
+            val availableFpSensorType = collectLastValue(underTest.availableFpSensorType)
+            runCurrent()
+
+            val callback = ArgumentCaptor.forClass(AuthController.Callback::class.java)
+            verify(authController).addCallback(callback.capture())
+            assertThat(availableFpSensorType()).isNull()
+
+            whenever(authController.isUdfpsSupported).thenReturn(true)
+            callback.value.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT)
+            assertThat(availableFpSensorType()).isEqualTo(BiometricType.UNDER_DISPLAY_FINGERPRINT)
         }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManagerTest.kt
deleted file mode 100644
index d55370b..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManagerTest.kt
+++ /dev/null
@@ -1,427 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.keyguard.data.repository
-
-import android.app.StatusBarManager.SESSION_KEYGUARD
-import android.content.pm.UserInfo
-import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_CANCELED
-import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT
-import android.hardware.biometrics.ComponentInfoInternal
-import android.hardware.face.FaceAuthenticateOptions
-import android.hardware.face.FaceManager
-import android.hardware.face.FaceSensorProperties
-import android.hardware.face.FaceSensorPropertiesInternal
-import android.os.CancellationSignal
-import androidx.test.filters.SmallTest
-import com.android.internal.logging.InstanceId.fakeInstanceId
-import com.android.internal.logging.UiEventLogger
-import com.android.keyguard.FaceAuthUiEvent
-import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
-import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
-import com.android.systemui.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.FlowValue
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.dump.logcatLogBuffer
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
-import com.android.systemui.log.FaceAuthenticationLogger
-import com.android.systemui.log.SessionTracker
-import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth.assertThat
-import java.io.PrintWriter
-import java.io.StringWriter
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestDispatcher
-import kotlinx.coroutines.test.TestScope
-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.ArgumentCaptor
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Captor
-import org.mockito.Mock
-import org.mockito.Mockito.clearInvocations
-import org.mockito.Mockito.isNull
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.MockitoAnnotations
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(JUnit4::class)
-class KeyguardFaceAuthManagerTest : SysuiTestCase() {
-    private lateinit var underTest: KeyguardFaceAuthManagerImpl
-
-    @Mock private lateinit var faceManager: FaceManager
-    @Mock private lateinit var bypassController: KeyguardBypassController
-    @Mock private lateinit var sessionTracker: SessionTracker
-    @Mock private lateinit var uiEventLogger: UiEventLogger
-    @Mock private lateinit var dumpManager: DumpManager
-
-    @Captor
-    private lateinit var authenticationCallback: ArgumentCaptor<FaceManager.AuthenticationCallback>
-    @Captor
-    private lateinit var detectionCallback: ArgumentCaptor<FaceManager.FaceDetectionCallback>
-    @Captor private lateinit var cancellationSignal: ArgumentCaptor<CancellationSignal>
-    @Captor
-    private lateinit var faceLockoutResetCallback: ArgumentCaptor<FaceManager.LockoutResetCallback>
-    private lateinit var testDispatcher: TestDispatcher
-
-    private lateinit var testScope: TestScope
-    private lateinit var fakeUserRepository: FakeUserRepository
-    private lateinit var authStatus: FlowValue<AuthenticationStatus?>
-    private lateinit var detectStatus: FlowValue<DetectionStatus?>
-    private lateinit var authRunning: FlowValue<Boolean?>
-    private lateinit var lockedOut: FlowValue<Boolean?>
-
-    @Before
-    fun setup() {
-        MockitoAnnotations.initMocks(this)
-        fakeUserRepository = FakeUserRepository()
-        fakeUserRepository.setUserInfos(listOf(currentUser))
-        testDispatcher = StandardTestDispatcher()
-        testScope = TestScope(testDispatcher)
-        whenever(sessionTracker.getSessionId(SESSION_KEYGUARD)).thenReturn(keyguardSessionId)
-        whenever(bypassController.bypassEnabled).thenReturn(true)
-        underTest = createFaceAuthManagerImpl(faceManager)
-    }
-
-    private fun createFaceAuthManagerImpl(
-        fmOverride: FaceManager? = faceManager,
-        bypassControllerOverride: KeyguardBypassController? = bypassController
-    ) =
-        KeyguardFaceAuthManagerImpl(
-            mContext,
-            fmOverride,
-            fakeUserRepository,
-            bypassControllerOverride,
-            testScope.backgroundScope,
-            testDispatcher,
-            sessionTracker,
-            uiEventLogger,
-            FaceAuthenticationLogger(logcatLogBuffer("KeyguardFaceAuthManagerLog")),
-            dumpManager,
-        )
-
-    @Test
-    fun faceAuthRunsAndProvidesAuthStatusUpdates() =
-        testScope.runTest {
-            testSetup(this)
-
-            FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER.extraInfo = 10
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
-            faceAuthenticateIsCalled()
-            uiEventIsLogged(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
-
-            assertThat(authRunning()).isTrue()
-
-            val successResult = successResult()
-            authenticationCallback.value.onAuthenticationSucceeded(successResult)
-
-            assertThat(authStatus()).isEqualTo(SuccessAuthenticationStatus(successResult))
-
-            assertThat(authRunning()).isFalse()
-        }
-
-    private fun uiEventIsLogged(faceAuthUiEvent: FaceAuthUiEvent) {
-        verify(uiEventLogger)
-            .logWithInstanceIdAndPosition(
-                faceAuthUiEvent,
-                0,
-                null,
-                keyguardSessionId,
-                faceAuthUiEvent.extraInfo
-            )
-    }
-
-    @Test
-    fun faceAuthDoesNotRunWhileItIsAlreadyRunning() =
-        testScope.runTest {
-            testSetup(this)
-
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
-            faceAuthenticateIsCalled()
-            clearInvocations(faceManager)
-            clearInvocations(uiEventLogger)
-
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
-            verifyNoMoreInteractions(faceManager)
-            verifyNoMoreInteractions(uiEventLogger)
-        }
-
-    @Test
-    fun faceLockoutStatusIsPropagated() =
-        testScope.runTest {
-            testSetup(this)
-            verify(faceManager).addLockoutResetCallback(faceLockoutResetCallback.capture())
-
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
-            faceAuthenticateIsCalled()
-
-            authenticationCallback.value.onAuthenticationError(
-                FACE_ERROR_LOCKOUT_PERMANENT,
-                "face locked out"
-            )
-
-            assertThat(lockedOut()).isTrue()
-
-            faceLockoutResetCallback.value.onLockoutReset(0)
-            assertThat(lockedOut()).isFalse()
-        }
-
-    @Test
-    fun faceDetectionSupportIsTheCorrectValue() =
-        testScope.runTest {
-            assertThat(createFaceAuthManagerImpl(fmOverride = null).isDetectionSupported).isFalse()
-
-            whenever(faceManager.sensorPropertiesInternal).thenReturn(null)
-            assertThat(createFaceAuthManagerImpl().isDetectionSupported).isFalse()
-
-            whenever(faceManager.sensorPropertiesInternal).thenReturn(listOf())
-            assertThat(createFaceAuthManagerImpl().isDetectionSupported).isFalse()
-
-            whenever(faceManager.sensorPropertiesInternal)
-                .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = false)))
-            assertThat(createFaceAuthManagerImpl().isDetectionSupported).isFalse()
-
-            whenever(faceManager.sensorPropertiesInternal)
-                .thenReturn(
-                    listOf(
-                        createFaceSensorProperties(supportsFaceDetection = false),
-                        createFaceSensorProperties(supportsFaceDetection = true)
-                    )
-                )
-            assertThat(createFaceAuthManagerImpl().isDetectionSupported).isFalse()
-
-            whenever(faceManager.sensorPropertiesInternal)
-                .thenReturn(
-                    listOf(
-                        createFaceSensorProperties(supportsFaceDetection = true),
-                        createFaceSensorProperties(supportsFaceDetection = false)
-                    )
-                )
-            assertThat(createFaceAuthManagerImpl().isDetectionSupported).isTrue()
-        }
-
-    @Test
-    fun cancelStopsFaceAuthentication() =
-        testScope.runTest {
-            testSetup(this)
-
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
-            faceAuthenticateIsCalled()
-
-            var wasAuthCancelled = false
-            cancellationSignal.value.setOnCancelListener { wasAuthCancelled = true }
-
-            underTest.cancel()
-            assertThat(wasAuthCancelled).isTrue()
-            assertThat(authRunning()).isFalse()
-        }
-
-    @Test
-    fun cancelInvokedWithoutFaceAuthRunningIsANoop() = testScope.runTest { underTest.cancel() }
-
-    @Test
-    fun faceDetectionRunsAndPropagatesDetectionStatus() =
-        testScope.runTest {
-            whenever(faceManager.sensorPropertiesInternal)
-                .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = true)))
-            underTest = createFaceAuthManagerImpl()
-            testSetup(this)
-
-            underTest.detect()
-            faceDetectIsCalled()
-
-            detectionCallback.value.onFaceDetected(1, 1, true)
-
-            assertThat(detectStatus()).isEqualTo(DetectionStatus(1, 1, true))
-        }
-
-    @Test
-    fun faceDetectDoesNotRunIfDetectionIsNotSupported() =
-        testScope.runTest {
-            whenever(faceManager.sensorPropertiesInternal)
-                .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = false)))
-            underTest = createFaceAuthManagerImpl()
-            testSetup(this)
-            clearInvocations(faceManager)
-
-            underTest.detect()
-
-            verify(faceManager, never()).detectFace(any(), any(), any())
-        }
-
-    @Test
-    fun faceAuthShouldWaitAndRunIfTriggeredWhileCancelling() =
-        testScope.runTest {
-            testSetup(this)
-
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
-            faceAuthenticateIsCalled()
-
-            // Enter cancelling state
-            underTest.cancel()
-            clearInvocations(faceManager)
-
-            // Auth is while cancelling.
-            underTest.authenticate(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
-            // Auth is not started
-            verifyNoMoreInteractions(faceManager)
-
-            // Auth is done cancelling.
-            authenticationCallback.value.onAuthenticationError(
-                FACE_ERROR_CANCELED,
-                "First auth attempt cancellation completed"
-            )
-            assertThat(authStatus())
-                .isEqualTo(
-                    ErrorAuthenticationStatus(
-                        FACE_ERROR_CANCELED,
-                        "First auth attempt cancellation completed"
-                    )
-                )
-
-            faceAuthenticateIsCalled()
-            uiEventIsLogged(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
-        }
-
-    @Test
-    fun faceAuthAutoCancelsAfterDefaultCancellationTimeout() =
-        testScope.runTest {
-            testSetup(this)
-
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
-            faceAuthenticateIsCalled()
-
-            clearInvocations(faceManager)
-            underTest.cancel()
-            advanceTimeBy(KeyguardFaceAuthManagerImpl.DEFAULT_CANCEL_SIGNAL_TIMEOUT + 1)
-
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
-            faceAuthenticateIsCalled()
-        }
-
-    @Test
-    fun faceHelpMessagesAreIgnoredBasedOnConfig() =
-        testScope.runTest {
-            overrideResource(
-                R.array.config_face_acquire_device_entry_ignorelist,
-                intArrayOf(10, 11)
-            )
-            underTest = createFaceAuthManagerImpl()
-            testSetup(this)
-
-            underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
-            faceAuthenticateIsCalled()
-
-            authenticationCallback.value.onAuthenticationHelp(9, "help msg")
-            authenticationCallback.value.onAuthenticationHelp(10, "Ignored help msg")
-            authenticationCallback.value.onAuthenticationHelp(11, "Ignored help msg")
-
-            assertThat(authStatus()).isEqualTo(HelpAuthenticationStatus(9, "help msg"))
-        }
-
-    @Test
-    fun dumpDoesNotErrorOutWhenFaceManagerOrBypassControllerIsNull() =
-        testScope.runTest {
-            fakeUserRepository.setSelectedUserInfo(currentUser)
-            underTest.dump(PrintWriter(StringWriter()), emptyArray())
-
-            underTest =
-                createFaceAuthManagerImpl(fmOverride = null, bypassControllerOverride = null)
-            fakeUserRepository.setSelectedUserInfo(currentUser)
-
-            underTest.dump(PrintWriter(StringWriter()), emptyArray())
-        }
-
-    private suspend fun testSetup(testScope: TestScope) {
-        with(testScope) {
-            authStatus = collectLastValue(underTest.authenticationStatus)
-            detectStatus = collectLastValue(underTest.detectionStatus)
-            authRunning = collectLastValue(underTest.isAuthRunning)
-            lockedOut = collectLastValue(underTest.isLockedOut)
-            fakeUserRepository.setSelectedUserInfo(currentUser)
-        }
-    }
-
-    private fun successResult() = FaceManager.AuthenticationResult(null, null, currentUserId, false)
-
-    private fun faceDetectIsCalled() {
-        verify(faceManager)
-            .detectFace(
-                cancellationSignal.capture(),
-                detectionCallback.capture(),
-                eq(FaceAuthenticateOptions.Builder().setUserId(currentUserId).build())
-            )
-    }
-
-    private fun faceAuthenticateIsCalled() {
-        verify(faceManager)
-            .authenticate(
-                isNull(),
-                cancellationSignal.capture(),
-                authenticationCallback.capture(),
-                isNull(),
-                eq(FaceAuthenticateOptions.Builder().setUserId(currentUserId).build())
-            )
-    }
-
-    private fun createFaceSensorProperties(
-        supportsFaceDetection: Boolean
-    ): FaceSensorPropertiesInternal {
-        val componentInfo =
-            listOf(
-                ComponentInfoInternal(
-                    "faceSensor" /* componentId */,
-                    "vendor/model/revision" /* hardwareVersion */,
-                    "1.01" /* firmwareVersion */,
-                    "00000001" /* serialNumber */,
-                    "" /* softwareVersion */
-                )
-            )
-        return FaceSensorPropertiesInternal(
-            0 /* id */,
-            FaceSensorProperties.STRENGTH_STRONG,
-            1 /* maxTemplatesAllowed */,
-            componentInfo,
-            FaceSensorProperties.TYPE_UNKNOWN,
-            supportsFaceDetection /* supportsFaceDetection */,
-            true /* supportsSelfIllumination */,
-            false /* resetLockoutRequiresChallenge */
-        )
-    }
-
-    companion object {
-        const val currentUserId = 1
-        val keyguardSessionId = fakeInstanceId(10)!!
-        val currentUser = UserInfo(currentUserId, "test user", 0)
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 7f30162..68d694a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -18,18 +18,16 @@
 package com.android.systemui.keyguard.domain.interactor
 
 import android.app.StatusBarManager
-import android.content.Context
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.flags.FakeFeatureFlags
 import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
+import com.android.systemui.keyguard.data.repository.FakeCommandQueue
 import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
-import com.android.systemui.settings.DisplayTracker
-import com.android.systemui.statusbar.CommandQueue
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.flow.onCompletion
 import kotlinx.coroutines.test.TestScope
@@ -38,7 +36,6 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.Mockito.mock
 import org.mockito.MockitoAnnotations
 
 @SmallTest
@@ -56,7 +53,7 @@
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) }
-        commandQueue = FakeCommandQueue(mock(Context::class.java), mock(DisplayTracker::class.java))
+        commandQueue = FakeCommandQueue()
         testScope = TestScope()
         repository = FakeKeyguardRepository()
         bouncerRepository = FakeKeyguardBouncerRepository()
@@ -174,22 +171,3 @@
             assertThat(secureCameraActive()).isFalse()
         }
 }
-
-class FakeCommandQueue(val context: Context, val displayTracker: DisplayTracker) :
-    CommandQueue(context, displayTracker) {
-    private val callbacks = mutableListOf<Callbacks>()
-
-    override fun addCallback(callback: Callbacks) {
-        callbacks.add(callback)
-    }
-
-    override fun removeCallback(callback: Callbacks) {
-        callbacks.remove(callback)
-    }
-
-    fun doForEachCallback(func: (callback: Callbacks) -> Unit) {
-        callbacks.forEach { func(it) }
-    }
-
-    fun callbackCount(): Int = callbacks.size
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java
index aa54a1c..447b333 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java
@@ -26,6 +26,7 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -37,6 +38,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.logging.InstanceId;
+import com.android.internal.logging.UiEventLogger;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -64,6 +66,8 @@
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock
     private KeyguardStateController mKeyguardStateController;
+    @Mock
+    private UiEventLogger mUiEventLogger;
 
     @Captor
     ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallbackCaptor;
@@ -87,7 +91,8 @@
                 mStatusBarService,
                 mAuthController,
                 mKeyguardUpdateMonitor,
-                mKeyguardStateController
+                mKeyguardStateController,
+                mUiEventLogger
         );
     }
 
@@ -238,6 +243,62 @@
                 eq(SESSION_KEYGUARD), any(InstanceId.class));
     }
 
+    @Test
+    public void uiEventLoggedOnEndSessionWhenDeviceStartsSleeping() throws RemoteException {
+        // GIVEN session tracker start
+        mSessionTracker.start();
+        captureKeyguardUpdateMonitorCallback();
+        captureKeyguardStateControllerCallback();
+
+        // GIVEN keyguard becomes visible (ie: from lockdown), so there's a valid keyguard
+        // session running
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        mKeyguardStateCallback.onKeyguardShowingChanged();
+
+        // WHEN device starts going to sleep
+        mKeyguardUpdateMonitorCallback.onStartedGoingToSleep(0);
+
+        // THEN UI event is logged
+        verify(mUiEventLogger).log(
+                eq(SessionTracker.SessionUiEvent.KEYGUARD_SESSION_END_GOING_TO_SLEEP),
+                any(InstanceId.class));
+    }
+
+    @Test
+    public void noUiEventLoggedOnEndSessionWhenDeviceStartsSleepingWithoutStartSession()
+            throws RemoteException {
+        // GIVEN session tracker start without any valid sessions
+        mSessionTracker.start();
+        captureKeyguardUpdateMonitorCallback();
+
+        // WHEN device starts going to sleep when there was no started sessions
+        mKeyguardUpdateMonitorCallback.onStartedGoingToSleep(0);
+
+        // THEN UI event is never logged
+        verify(mUiEventLogger, never()).log(
+                eq(SessionTracker.SessionUiEvent.KEYGUARD_SESSION_END_GOING_TO_SLEEP),
+                any(InstanceId.class));
+    }
+
+    @Test
+    public void uiEventLoggedOnEndSessionWhenKeyguardGoingAway() throws RemoteException {
+        // GIVEN session tracker started w/o any sessions
+        mSessionTracker.start();
+        captureKeyguardUpdateMonitorCallback();
+        captureKeyguardStateControllerCallback();
+
+        // WHEN keyguard was showing and now it's not
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+        mKeyguardStateCallback.onKeyguardShowingChanged();
+        when(mKeyguardStateController.isShowing()).thenReturn(false);
+        mKeyguardStateCallback.onKeyguardShowingChanged();
+
+        // THEN UI event is logged
+        verify(mUiEventLogger).log(
+                eq(SessionTracker.SessionUiEvent.KEYGUARD_SESSION_END_KEYGUARD_GOING_AWAY),
+                any(InstanceId.class));
+    }
+
     void captureKeyguardUpdateMonitorCallback() {
         verify(mKeyguardUpdateMonitor).registerCallback(
                 mKeyguardUpdateMonitorCallbackCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index fbe089a..ba29ca5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -369,6 +369,39 @@
 
         verifyZeroInteractions(context, bubbles, eventLogger)
     }
+
+    @Test
+    fun showNoteTask_keyboardShortcut_shouldStartActivity() {
+        val expectedInfo =
+                NOTE_TASK_INFO.copy(
+                        entryPoint = NoteTaskEntryPoint.KEYBOARD_SHORTCUT,
+                        isKeyguardLocked = true,
+                )
+        whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
+        whenever(resolver.resolveInfo(any(), any())).thenReturn(expectedInfo)
+
+        createNoteTaskController()
+                .showNoteTask(
+                        entryPoint = expectedInfo.entryPoint!!,
+                )
+
+        val intentCaptor = argumentCaptor<Intent>()
+        val userCaptor = argumentCaptor<UserHandle>()
+        verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
+        intentCaptor.value.let { intent ->
+            assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
+            assertThat(intent.`package`).isEqualTo(NOTE_TASK_PACKAGE_NAME)
+            assertThat(intent.flags and FLAG_ACTIVITY_NEW_TASK).isEqualTo(FLAG_ACTIVITY_NEW_TASK)
+            assertThat(intent.flags and FLAG_ACTIVITY_MULTIPLE_TASK)
+                    .isEqualTo(FLAG_ACTIVITY_MULTIPLE_TASK)
+            assertThat(intent.flags and FLAG_ACTIVITY_NEW_DOCUMENT)
+                    .isEqualTo(FLAG_ACTIVITY_NEW_DOCUMENT)
+            assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, true)).isFalse()
+        }
+        assertThat(userCaptor.value).isEqualTo(userTracker.userHandle)
+        verify(eventLogger).logNoteTaskOpened(expectedInfo)
+        verifyZeroInteractions(bubbles)
+    }
     // endregion
 
     // region setNoteTaskShortcutEnabled
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
index cd67e8d..ec4daee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
@@ -98,14 +98,24 @@
     // region handleSystemKey
     @Test
     fun handleSystemKey_receiveValidSystemKey_shouldShowNoteTask() {
-        createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL)
+        createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent(KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL))
 
         verify(controller).showNoteTask(entryPoint = NoteTaskEntryPoint.TAIL_BUTTON)
     }
 
     @Test
+    fun handleSystemKey_receiveKeyboardShortcut_shouldShowNoteTask() {
+        createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_N, 0, KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON))
+
+        verify(controller).showNoteTask(entryPoint = NoteTaskEntryPoint.KEYBOARD_SHORTCUT)
+    }
+    
+    @Test
     fun handleSystemKey_receiveInvalidSystemKey_shouldDoNothing() {
-        createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent.KEYCODE_UNKNOWN)
+        createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent(KeyEvent.ACTION_DOWN,
+                KeyEvent.KEYCODE_UNKNOWN))
 
         verifyZeroInteractions(controller)
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java
index 6f54f62..f5a3bec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java
@@ -111,8 +111,6 @@
         MockitoAnnotations.initMocks(this);
 
         mDeviceConfigProxyFake = new DeviceConfigProxyFake();
-        mDeviceConfigProxyFake.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED, "true", false);
         mSystemClock = new FakeSystemClock();
         mMainExecutor = new FakeExecutor(mSystemClock);
         mBackgroundExecutor = new FakeExecutor(mSystemClock);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java
index 46af89e..9ca7a85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java
@@ -40,6 +40,7 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository;
 import com.android.systemui.settings.UserTracker;
 
 import org.junit.After;
@@ -64,6 +65,8 @@
     private QSHost mQSHost;
     @Mock
     private Context mMockContext;
+    @Mock
+    private CustomTileAddedRepository mCustomTileAddedRepository;
 
     private HandlerThread mThread;
     private Handler mHandler;
@@ -86,8 +89,9 @@
 
         mComponentName = new ComponentName(mContext, TileServiceManagerTest.class);
         when(mTileLifecycle.getComponent()).thenReturn(mComponentName);
+
         mTileServiceManager = new TileServiceManager(mTileServices, mHandler, mUserTracker,
-                mTileLifecycle);
+                mCustomTileAddedRepository, mTileLifecycle);
     }
 
     @After
@@ -98,28 +102,34 @@
 
     @Test
     public void testSetTileAddedIfNotAdded() {
-        when(mQSHost.isTileAdded(eq(mComponentName), anyInt())).thenReturn(false);
+        when(mCustomTileAddedRepository.isTileAdded(eq(mComponentName), anyInt()))
+                .thenReturn(false);
         mTileServiceManager.startLifecycleManagerAndAddTile();
 
-        verify(mQSHost).setTileAdded(mComponentName, mUserTracker.getUserId(), true);
+        verify(mCustomTileAddedRepository)
+                .setTileAdded(mComponentName, mUserTracker.getUserId(), true);
     }
 
     @Test
     public void testNotSetTileAddedIfAdded() {
-        when(mQSHost.isTileAdded(eq(mComponentName), anyInt())).thenReturn(true);
+        when(mCustomTileAddedRepository.isTileAdded(eq(mComponentName), anyInt()))
+                .thenReturn(true);
         mTileServiceManager.startLifecycleManagerAndAddTile();
 
-        verify(mQSHost, never()).setTileAdded(eq(mComponentName), anyInt(), eq(true));
+        verify(mCustomTileAddedRepository, never())
+                .setTileAdded(eq(mComponentName), anyInt(), eq(true));
     }
 
     @Test
     public void testSetTileAddedCorrectUser() {
         int user = 10;
         when(mUserTracker.getUserId()).thenReturn(user);
-        when(mQSHost.isTileAdded(eq(mComponentName), anyInt())).thenReturn(false);
+        when(mCustomTileAddedRepository.isTileAdded(eq(mComponentName), anyInt()))
+                .thenReturn(false);
         mTileServiceManager.startLifecycleManagerAndAddTile();
 
-        verify(mQSHost).setTileAdded(mComponentName, user, true);
+        verify(mCustomTileAddedRepository)
+                .setTileAdded(mComponentName, user, true);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index fb93367..12b5656 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -42,6 +42,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository;
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.CommandQueue;
@@ -98,6 +99,8 @@
     private PanelInteractor mPanelInteractor;
     @Captor
     private ArgumentCaptor<CommandQueue.Callbacks> mCallbacksArgumentCaptor;
+    @Mock
+    private CustomTileAddedRepository mCustomTileAddedRepository;
 
     @Before
     public void setUp() throws Exception {
@@ -115,7 +118,7 @@
 
         mTileService = new TestTileServices(mQSHost, provider, mBroadcastDispatcher,
                 mUserTracker, mKeyguardStateController, mCommandQueue, mStatusBarIconController,
-                mPanelInteractor);
+                mPanelInteractor, mCustomTileAddedRepository);
     }
 
     @After
@@ -293,9 +296,11 @@
         TestTileServices(QSHost host, Provider<Handler> handlerProvider,
                 BroadcastDispatcher broadcastDispatcher, UserTracker userTracker,
                 KeyguardStateController keyguardStateController, CommandQueue commandQueue,
-                StatusBarIconController statusBarIconController, PanelInteractor panelInteractor) {
+                StatusBarIconController statusBarIconController, PanelInteractor panelInteractor,
+                CustomTileAddedRepository customTileAddedRepository) {
             super(host, handlerProvider, broadcastDispatcher, userTracker, keyguardStateController,
-                    commandQueue, statusBarIconController, panelInteractor);
+                    commandQueue, statusBarIconController, panelInteractor,
+                    customTileAddedRepository);
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
index 0b9fbd9..59f0d96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
@@ -259,7 +259,6 @@
         val securityController = FakeSecurityController()
         val fgsManagerController =
             FakeFgsManagerController(
-                isAvailable = true,
                 showFooterDot = false,
                 numRunningPackages = 0,
             )
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
new file mode 100644
index 0000000..d7ab903
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
@@ -0,0 +1,156 @@
+/*
+ * 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.qs.pipeline.data.repository
+
+import android.content.ComponentName
+import android.content.SharedPreferences
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.util.FakeSharedPreferences
+import com.google.common.truth.Truth.assertThat
+import java.io.File
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class CustomTileAddedSharedPreferencesRepositoryTest : SysuiTestCase() {
+
+    private lateinit var underTest: CustomTileAddedSharedPrefsRepository
+
+    @Test
+    fun setTileAdded_inSharedPreferences() {
+        val userId = 0
+        val sharedPrefs = FakeSharedPreferences()
+        val userFileManager = FakeUserFileManager(mapOf(userId to sharedPrefs))
+
+        underTest = CustomTileAddedSharedPrefsRepository(userFileManager)
+
+        underTest.setTileAdded(TEST_COMPONENT, userId, added = true)
+        assertThat(sharedPrefs.getForComponentName(TEST_COMPONENT)).isTrue()
+
+        underTest.setTileAdded(TEST_COMPONENT, userId, added = false)
+        assertThat(sharedPrefs.getForComponentName(TEST_COMPONENT)).isFalse()
+    }
+
+    @Test
+    fun setTileAdded_differentComponents() {
+        val userId = 0
+        val sharedPrefs = FakeSharedPreferences()
+        val userFileManager = FakeUserFileManager(mapOf(userId to sharedPrefs))
+
+        underTest = CustomTileAddedSharedPrefsRepository(userFileManager)
+
+        underTest.setTileAdded(TEST_COMPONENT, userId, added = true)
+
+        assertThat(sharedPrefs.getForComponentName(TEST_COMPONENT)).isTrue()
+        assertThat(sharedPrefs.getForComponentName(OTHER_TEST_COMPONENT)).isFalse()
+    }
+
+    @Test
+    fun setTileAdded_differentUsers() {
+        val sharedPrefs0 = FakeSharedPreferences()
+        val sharedPrefs1 = FakeSharedPreferences()
+        val userFileManager = FakeUserFileManager(mapOf(0 to sharedPrefs0, 1 to sharedPrefs1))
+
+        underTest = CustomTileAddedSharedPrefsRepository(userFileManager)
+
+        underTest.setTileAdded(TEST_COMPONENT, userId = 1, added = true)
+
+        assertThat(sharedPrefs0.getForComponentName(TEST_COMPONENT)).isFalse()
+        assertThat(sharedPrefs1.getForComponentName(TEST_COMPONENT)).isTrue()
+    }
+
+    @Test
+    fun isTileAdded_fromSharedPreferences() {
+        val userId = 0
+        val sharedPrefs = FakeSharedPreferences()
+        val userFileManager = FakeUserFileManager(mapOf(userId to sharedPrefs))
+
+        underTest = CustomTileAddedSharedPrefsRepository(userFileManager)
+
+        assertThat(underTest.isTileAdded(TEST_COMPONENT, userId)).isFalse()
+
+        sharedPrefs.setForComponentName(TEST_COMPONENT, true)
+        assertThat(underTest.isTileAdded(TEST_COMPONENT, userId)).isTrue()
+
+        sharedPrefs.setForComponentName(TEST_COMPONENT, false)
+        assertThat(underTest.isTileAdded(TEST_COMPONENT, userId)).isFalse()
+    }
+
+    @Test
+    fun isTileAdded_differentComponents() {
+        val userId = 0
+        val sharedPrefs = FakeSharedPreferences()
+        val userFileManager = FakeUserFileManager(mapOf(userId to sharedPrefs))
+
+        underTest = CustomTileAddedSharedPrefsRepository(userFileManager)
+
+        sharedPrefs.setForComponentName(OTHER_TEST_COMPONENT, true)
+
+        assertThat(underTest.isTileAdded(TEST_COMPONENT, userId)).isFalse()
+        assertThat(underTest.isTileAdded(OTHER_TEST_COMPONENT, userId)).isTrue()
+    }
+
+    @Test
+    fun isTileAdded_differentUsers() {
+        val sharedPrefs0 = FakeSharedPreferences()
+        val sharedPrefs1 = FakeSharedPreferences()
+        val userFileManager = FakeUserFileManager(mapOf(0 to sharedPrefs0, 1 to sharedPrefs1))
+
+        underTest = CustomTileAddedSharedPrefsRepository(userFileManager)
+
+        sharedPrefs1.setForComponentName(TEST_COMPONENT, true)
+
+        assertThat(underTest.isTileAdded(TEST_COMPONENT, userId = 0)).isFalse()
+        assertThat(underTest.isTileAdded(TEST_COMPONENT, userId = 1)).isTrue()
+    }
+
+    private fun SharedPreferences.getForComponentName(componentName: ComponentName): Boolean {
+        return getBoolean(componentName.flattenToString(), false)
+    }
+
+    private fun SharedPreferences.setForComponentName(
+        componentName: ComponentName,
+        value: Boolean
+    ) {
+        edit().putBoolean(componentName.flattenToString(), value).commit()
+    }
+
+    companion object {
+        private val TEST_COMPONENT = ComponentName("pkg", "cls")
+        private val OTHER_TEST_COMPONENT = ComponentName("pkg", "other")
+    }
+}
+
+private const val FILE_NAME = "tiles_prefs"
+
+private class FakeUserFileManager(private val sharedPrefs: Map<Int, SharedPreferences>) :
+    UserFileManager {
+    override fun getFile(fileName: String, userId: Int): File {
+        throw UnsupportedOperationException()
+    }
+
+    override fun getSharedPreferences(fileName: String, mode: Int, userId: Int): SharedPreferences {
+        if (fileName != FILE_NAME) {
+            throw IllegalArgumentException("Preference files must be $FILE_NAME")
+        }
+        return sharedPrefs.getValue(userId)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
index d3ec1dd..28aeba4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
@@ -317,6 +317,26 @@
     }
 
     @Test
+    fun testDisableByPolicyThenRemoved_changesColor() {
+        val stateActive = QSTile.State()
+        stateActive.state = Tile.STATE_ACTIVE
+
+        val stateDisabledByPolicy = stateActive.copy()
+        stateDisabledByPolicy.disabledByPolicy = true
+
+        tileView.changeState(stateActive)
+        val activeColors = tileView.getCurrentColors()
+
+        tileView.changeState(stateDisabledByPolicy)
+        // It has unavailable colors
+        assertThat(tileView.getCurrentColors()).isNotEqualTo(activeColors)
+
+        // When we get back to not disabled by policy tile, it should go back to active colors
+        tileView.changeState(stateActive)
+        assertThat(tileView.getCurrentColors()).containsExactlyElementsIn(activeColors)
+    }
+
+    @Test
     fun testDisabledByPolicy_secondaryLabelText() {
         val testA11yLabel = "TEST_LABEL"
         context.orCreateTestableResources
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
index 47d88a5..77f7426 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
@@ -23,28 +23,21 @@
 import android.graphics.Bitmap
 import android.graphics.Bitmap.Config.HARDWARE
 import android.graphics.ColorSpace
-import android.graphics.Insets
-import android.graphics.Rect
 import android.hardware.HardwareBuffer
 import android.os.UserHandle
 import android.os.UserManager
 import android.testing.AndroidTestingRunner
 import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER
-import android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW
 import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
-import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
 import androidx.test.filters.SmallTest
 import com.android.internal.logging.testing.UiEventLoggerFake
 import com.android.internal.util.ScreenshotRequest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags.SCREENSHOT_METADATA_REFACTOR
 import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_CAPTURE_FAILED
 import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_OTHER
-import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_REQUESTED_OVERVIEW
 import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
 import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argThat
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.mockito.mock
 import com.android.systemui.util.mockito.whenever
@@ -61,9 +54,6 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyZeroInteractions
 
-private const val USER_ID = 1
-private const val TASK_ID = 11
-
 @RunWith(AndroidTestingRunner::class)
 @SmallTest
 class TakeScreenshotServiceTest : SysuiTestCase() {
@@ -123,9 +113,6 @@
             .whenever(requestProcessor)
             .processAsync(/* screenshot= */ any(ScreenshotData::class.java), /* callback= */ any())
 
-        // Flipped in selected test cases
-        flags.set(SCREENSHOT_METADATA_REFACTOR, false)
-
         service.attach(
             mContext,
             /* thread = */ null,
@@ -158,39 +145,6 @@
         service.handleRequest(request, { /* onSaved */}, callback)
 
         verify(controller, times(1))
-            .takeScreenshotFullscreen(
-                eq(topComponent),
-                /* onSavedListener = */ any(),
-                /* requestCallback = */ any()
-            )
-
-        assertEquals("Expected one UiEvent", 1, eventLogger.numLogs())
-        val logEvent = eventLogger.get(0)
-
-        assertEquals(
-            "Expected SCREENSHOT_REQUESTED UiEvent",
-            logEvent.eventId,
-            SCREENSHOT_REQUESTED_KEY_OTHER.id
-        )
-        assertEquals(
-            "Expected supplied package name",
-            topComponent.packageName,
-            eventLogger.get(0).packageName
-        )
-    }
-
-    @Test
-    fun takeScreenshotFullscreen_screenshotDataEnabled() {
-        flags.set(SCREENSHOT_METADATA_REFACTOR, true)
-
-        val request =
-            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
-                .setTopComponent(topComponent)
-                .build()
-
-        service.handleRequest(request, { /* onSaved */}, callback)
-
-        verify(controller, times(1))
             .handleScreenshot(
                 eq(ScreenshotData.fromRequest(request)),
                 /* onSavedListener = */ any(),
@@ -213,53 +167,7 @@
     }
 
     @Test
-    fun takeScreenshotProvidedImage() {
-        val bounds = Rect(50, 50, 150, 150)
-        val bitmap = makeHardwareBitmap(100, 100)
-
-        val request =
-            ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OVERVIEW)
-                .setTopComponent(topComponent)
-                .setTaskId(TASK_ID)
-                .setUserId(USER_ID)
-                .setBitmap(bitmap)
-                .setBoundsOnScreen(bounds)
-                .setInsets(Insets.NONE)
-                .build()
-
-        service.handleRequest(request, { /* onSaved */}, callback)
-
-        verify(controller, times(1))
-            .handleImageAsScreenshot(
-                argThat { b -> b.equalsHardwareBitmap(bitmap) },
-                eq(bounds),
-                eq(Insets.NONE),
-                eq(TASK_ID),
-                eq(USER_ID),
-                eq(topComponent),
-                /* onSavedListener = */ any(),
-                /* requestCallback = */ any()
-            )
-
-        assertEquals("Expected one UiEvent", 1, eventLogger.numLogs())
-        val logEvent = eventLogger.get(0)
-
-        assertEquals(
-            "Expected SCREENSHOT_REQUESTED_* UiEvent",
-            logEvent.eventId,
-            SCREENSHOT_REQUESTED_OVERVIEW.id
-        )
-        assertEquals(
-            "Expected supplied package name",
-            topComponent.packageName,
-            eventLogger.get(0).packageName
-        )
-    }
-
-    @Test
     fun takeScreenshotFullscreen_userLocked() {
-        flags.set(SCREENSHOT_METADATA_REFACTOR, true)
-
         whenever(userManager.isUserUnlocked).thenReturn(false)
 
         val request =
@@ -300,8 +208,6 @@
 
     @Test
     fun takeScreenshotFullscreen_screenCaptureDisabled_allUsers() {
-        flags.set(SCREENSHOT_METADATA_REFACTOR, true)
-
         whenever(devicePolicyManager.getScreenCaptureDisabled(isNull(), eq(UserHandle.USER_ALL)))
             .thenReturn(true)
 
@@ -350,143 +256,7 @@
     }
 
     @Test
-    fun takeScreenshotFullscreen_userLocked_metadataDisabled() {
-        flags.set(SCREENSHOT_METADATA_REFACTOR, false)
-        whenever(userManager.isUserUnlocked).thenReturn(false)
-
-        val request =
-            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
-                .setTopComponent(topComponent)
-                .build()
-
-        service.handleRequest(request, { /* onSaved */}, callback)
-
-        verify(notificationsController, times(1)).notifyScreenshotError(anyInt())
-        verify(callback, times(1)).reportError()
-        verifyZeroInteractions(controller)
-
-        assertEquals("Expected two UiEvents", 2, eventLogger.numLogs())
-        val requestEvent = eventLogger.get(0)
-        assertEquals(
-            "Expected SCREENSHOT_REQUESTED_* UiEvent",
-            SCREENSHOT_REQUESTED_KEY_OTHER.id,
-            requestEvent.eventId
-        )
-        assertEquals(
-            "Expected supplied package name",
-            topComponent.packageName,
-            requestEvent.packageName
-        )
-        val failureEvent = eventLogger.get(1)
-        assertEquals(
-            "Expected SCREENSHOT_CAPTURE_FAILED UiEvent",
-            SCREENSHOT_CAPTURE_FAILED.id,
-            failureEvent.eventId
-        )
-        assertEquals(
-            "Expected supplied package name",
-            topComponent.packageName,
-            failureEvent.packageName
-        )
-    }
-
-    @Test
-    fun takeScreenshotFullscreen_screenCaptureDisabled_allUsers_metadataDisabled() {
-        flags.set(SCREENSHOT_METADATA_REFACTOR, false)
-
-        whenever(devicePolicyManager.getScreenCaptureDisabled(isNull(), eq(UserHandle.USER_ALL)))
-            .thenReturn(true)
-
-        whenever(
-                devicePolicyResourcesManager.getString(
-                    eq(SCREENSHOT_BLOCKED_BY_ADMIN),
-                    /* Supplier<String> */
-                    any(),
-                )
-            )
-            .thenReturn("SCREENSHOT_BLOCKED_BY_ADMIN")
-
-        val request =
-            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
-                .setTopComponent(topComponent)
-                .build()
-
-        service.handleRequest(request, { /* onSaved */}, callback)
-
-        // error shown: Toast.makeText(...).show(), untestable
-        verify(callback, times(1)).reportError()
-        verifyZeroInteractions(controller)
-        assertEquals("Expected two UiEvents", 2, eventLogger.numLogs())
-        val requestEvent = eventLogger.get(0)
-        assertEquals(
-            "Expected SCREENSHOT_REQUESTED_* UiEvent",
-            SCREENSHOT_REQUESTED_KEY_OTHER.id,
-            requestEvent.eventId
-        )
-        assertEquals(
-            "Expected supplied package name",
-            topComponent.packageName,
-            requestEvent.packageName
-        )
-        val failureEvent = eventLogger.get(1)
-        assertEquals(
-            "Expected SCREENSHOT_CAPTURE_FAILED UiEvent",
-            SCREENSHOT_CAPTURE_FAILED.id,
-            failureEvent.eventId
-        )
-        assertEquals(
-            "Expected supplied package name",
-            topComponent.packageName,
-            failureEvent.packageName
-        )
-    }
-
-    @Test
-    fun takeScreenshot_workProfile_nullBitmap_metadataDisabled() {
-        flags.set(SCREENSHOT_METADATA_REFACTOR, false)
-
-        val request =
-            ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
-                .setTopComponent(topComponent)
-                .build()
-
-        doThrow(IllegalStateException::class.java)
-            .whenever(requestProcessor)
-            .processAsync(any(ScreenshotRequest::class.java), any())
-
-        service.handleRequest(request, { /* onSaved */}, callback)
-
-        verify(callback, times(1)).reportError()
-        verify(notificationsController, times(1)).notifyScreenshotError(anyInt())
-        verifyZeroInteractions(controller)
-        assertEquals("Expected two UiEvents", 2, eventLogger.numLogs())
-        val requestEvent = eventLogger.get(0)
-        assertEquals(
-            "Expected SCREENSHOT_REQUESTED_* UiEvent",
-            SCREENSHOT_REQUESTED_KEY_OTHER.id,
-            requestEvent.eventId
-        )
-        assertEquals(
-            "Expected supplied package name",
-            topComponent.packageName,
-            requestEvent.packageName
-        )
-        val failureEvent = eventLogger.get(1)
-        assertEquals(
-            "Expected SCREENSHOT_CAPTURE_FAILED UiEvent",
-            SCREENSHOT_CAPTURE_FAILED.id,
-            failureEvent.eventId
-        )
-        assertEquals(
-            "Expected supplied package name",
-            topComponent.packageName,
-            failureEvent.packageName
-        )
-    }
-    @Test
     fun takeScreenshot_workProfile_nullBitmap() {
-        flags.set(SCREENSHOT_METADATA_REFACTOR, true)
-
         val request =
             ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
                 .setTopComponent(topComponent)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index 374aae1..78f5bf2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -122,7 +122,7 @@
             isEnabled = true,
             handleAllUsers = true,
             defaultClockProvider = fakeDefaultProvider,
-            keepAllLoaded = true,
+            keepAllLoaded = false,
             subTag = "Test",
         ) {
             override fun querySettings() { }
@@ -154,8 +154,8 @@
         pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle)
         val list = registry.getClocks()
         assertEquals(
-            list,
-            listOf(
+            list.toSet(),
+            setOf(
                 ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
                 ClockMetadata("clock_1", "clock 1"),
                 ClockMetadata("clock_2", "clock 2"),
@@ -187,8 +187,8 @@
         pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle2)
         val list = registry.getClocks()
         assertEquals(
-            list,
-            listOf(
+            list.toSet(),
+            setOf(
                 ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
                 ClockMetadata("clock_1", "clock 1"),
                 ClockMetadata("clock_2", "clock 2")
@@ -293,6 +293,53 @@
         assertEquals(4, listChangeCallCount)
     }
 
+    @Test
+    fun pluginAddRemove_concurrentModification() {
+        val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
+        val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
+        val mockPluginLifecycle3 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
+        val mockPluginLifecycle4 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
+        val plugin1 = FakeClockPlugin().addClock("clock_1", "clock 1")
+        val plugin2 = FakeClockPlugin().addClock("clock_2", "clock 2")
+        val plugin3 = FakeClockPlugin().addClock("clock_3", "clock 3")
+        val plugin4 = FakeClockPlugin().addClock("clock_4", "clock 4")
+        whenever(mockPluginLifecycle1.isLoaded).thenReturn(true)
+        whenever(mockPluginLifecycle2.isLoaded).thenReturn(true)
+        whenever(mockPluginLifecycle3.isLoaded).thenReturn(true)
+        whenever(mockPluginLifecycle4.isLoaded).thenReturn(true)
+
+        // Set the current clock to the final clock to load
+        registry.applySettings(ClockSettings("clock_4", null))
+        scheduler.runCurrent()
+
+        // When ClockRegistry attempts to unload a plugin, we at that point decide to load and
+        // unload other plugins. This causes ClockRegistry to modify the list of available clock
+        // plugins while it is being iterated over. In production this happens as a result of a
+        // thread race, instead of synchronously like it does here.
+        whenever(mockPluginLifecycle2.unloadPlugin()).then {
+            pluginListener.onPluginDetached(mockPluginLifecycle1)
+            pluginListener.onPluginLoaded(plugin4, mockContext, mockPluginLifecycle4)
+        }
+
+        // Load initial plugins
+        pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle1)
+        pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle2)
+        pluginListener.onPluginLoaded(plugin3, mockContext, mockPluginLifecycle3)
+
+        // Repeatedly verify the loaded providers to get final state
+        registry.verifyLoadedProviders()
+        scheduler.runCurrent()
+        registry.verifyLoadedProviders()
+        scheduler.runCurrent()
+
+        // Verify all plugins were correctly loaded into the registry
+        assertEquals(registry.getClocks().toSet(), setOf(
+            ClockMetadata("DEFAULT", "Default Clock"),
+            ClockMetadata("clock_2", "clock 2"),
+            ClockMetadata("clock_3", "clock 3"),
+            ClockMetadata("clock_4", "clock 4")
+        ))
+    }
 
     @Test
     fun jsonDeserialization_gotExpectedObject() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index f581154..f4cd383 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -33,6 +33,7 @@
 import android.hardware.biometrics.PromptInfo;
 import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
 import android.os.Bundle;
+import android.view.KeyEvent;
 import android.view.WindowInsets;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowInsetsController.Appearance;
@@ -397,9 +398,10 @@
 
     @Test
     public void testHandleSysKey() {
-        mCommandQueue.handleSystemKey(1);
+        KeyEvent testEvent = new KeyEvent(1, 1);
+        mCommandQueue.handleSystemKey(testEvent);
         waitForIdleSync();
-        verify(mCallbacks).handleSystemKey(eq(1));
+        verify(mCallbacks).handleSystemKey(eq(testEvent));
     }
 
     @Test
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
index d8b3270..65735f0 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt
@@ -21,7 +21,6 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.flowOf
 
 class FakeBiometricSettingsRepository : BiometricSettingsRepository {
 
@@ -39,12 +38,17 @@
     private val _isStrongBiometricAllowed = MutableStateFlow(false)
     override val isStrongBiometricAllowed = _isStrongBiometricAllowed.asStateFlow()
 
+    private val _isNonStrongBiometricAllowed = MutableStateFlow(false)
+    override val isNonStrongBiometricAllowed: StateFlow<Boolean>
+        get() = _isNonStrongBiometricAllowed
+
     private val _isFingerprintEnabledByDevicePolicy = MutableStateFlow(false)
     override val isFingerprintEnabledByDevicePolicy =
         _isFingerprintEnabledByDevicePolicy.asStateFlow()
 
+    private val _isFaceAuthSupportedInCurrentPosture = MutableStateFlow(false)
     override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean>
-        get() = flowOf(true)
+        get() = _isFaceAuthSupportedInCurrentPosture
 
     private val _isCurrentUserInLockdown = MutableStateFlow(false)
     override val isCurrentUserInLockdown: Flow<Boolean>
@@ -66,7 +70,19 @@
         _isFaceEnrolled.value = isFaceEnrolled
     }
 
+    fun setIsFaceAuthSupportedInCurrentPosture(value: Boolean) {
+        _isFaceAuthSupportedInCurrentPosture.value = value
+    }
+
     fun setIsFaceAuthEnabled(enabled: Boolean) {
         _isFaceAuthEnabled.value = enabled
     }
+
+    fun setIsUserInLockdown(value: Boolean) {
+        _isCurrentUserInLockdown.value = value
+    }
+
+    fun setIsNonStrongBiometricAllowed(value: Boolean) {
+        _isNonStrongBiometricAllowed.value = value
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeCommandQueue.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeCommandQueue.kt
new file mode 100644
index 0000000..fe94117
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeCommandQueue.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.content.Context
+import com.android.systemui.settings.DisplayTracker
+import com.android.systemui.statusbar.CommandQueue
+import org.mockito.Mockito.mock
+
+class FakeCommandQueue : CommandQueue(mock(Context::class.java), mock(DisplayTracker::class.java)) {
+    private val callbacks = mutableListOf<Callbacks>()
+
+    override fun addCallback(callback: Callbacks) {
+        callbacks.add(callback)
+    }
+
+    override fun removeCallback(callback: Callbacks) {
+        callbacks.remove(callback)
+    }
+
+    fun doForEachCallback(func: (callback: Callbacks) -> Unit) {
+        callbacks.forEach { func(it) }
+    }
+
+    fun callbackCount(): Int = callbacks.size
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
index 00b1a40..4bfd3d6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
@@ -30,10 +30,19 @@
     override val isRunning: Flow<Boolean>
         get() = _isRunning
 
-    override val availableFpSensorType: BiometricType?
-        get() = null
+    private var fpSensorType = MutableStateFlow<BiometricType?>(null)
+    override val availableFpSensorType: Flow<BiometricType?>
+        get() = fpSensorType
 
     fun setLockedOut(lockedOut: Boolean) {
         _isLockedOut.value = lockedOut
     }
+
+    fun setIsRunning(value: Boolean) {
+        _isRunning.value = value
+    }
+
+    fun setAvailableFpSensorType(value: BiometricType?) {
+        fpSensorType.value = value
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt
index 1dda472..8a6d2aa 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt
@@ -48,8 +48,6 @@
     override val showMessage = _showMessage.asStateFlow()
     private val _resourceUpdateRequests = MutableStateFlow(false)
     override val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow()
-    override val bouncerPromptReason = 0
-    override val bouncerErrorMessage: CharSequence? = null
     private val _isAlternateBouncerVisible = MutableStateFlow(false)
     override val alternateBouncerVisible = _isAlternateBouncerVisible.asStateFlow()
     override var lastAlternateBouncerVisibleTime: Long = 0L
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 194ed02..d411590 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -129,6 +129,10 @@
         _isKeyguardShowing.value = isShowing
     }
 
+    fun setKeyguardGoingAway(isGoingAway: Boolean) {
+        _isKeyguardGoingAway.value = isGoingAway
+    }
+
     fun setKeyguardOccluded(isOccluded: Boolean) {
         _isKeyguardOccluded.value = isOccluded
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt
new file mode 100644
index 0000000..6690de8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeTrustRepository : TrustRepository {
+    private val _isCurrentUserTrusted = MutableStateFlow(false)
+    override val isCurrentUserTrusted: Flow<Boolean>
+        get() = _isCurrentUserTrusted
+
+    fun setCurrentUserTrusted(trust: Boolean) {
+        _isCurrentUserTrusted.value = trust
+    }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt
index ced7955..9ff7dd5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt
@@ -23,11 +23,9 @@
 
 /** A fake [FgsManagerController] to be used in tests. */
 class FakeFgsManagerController(
-    isAvailable: Boolean = true,
     showFooterDot: Boolean = false,
     numRunningPackages: Int = 0,
 ) : FgsManagerController {
-    override val isAvailable: MutableStateFlow<Boolean> = MutableStateFlow(isAvailable)
 
     override var numRunningPackages = numRunningPackages
         set(value) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
index 53bb340..fbc2381 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
@@ -100,4 +100,8 @@
     fun setGuestUserAutoCreated(value: Boolean) {
         _isGuestUserAutoCreated = value
     }
+
+    fun setUserSwitching(value: Boolean) {
+        _userSwitchingInProgress.value = value
+    }
 }
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
index f3a949d..dd7d38f 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -133,18 +133,6 @@
             @UserIdInt int userId, int associationId) {
         final AssociationInfo association = resolveAssociation(packageName, userId, associationId);
 
-        // Check if the request's data type has been requested before.
-        List<SystemDataTransferRequest> storedRequests =
-                mSystemDataTransferRequestStore.readRequestsByAssociationId(userId,
-                        associationId);
-        for (SystemDataTransferRequest storedRequest : storedRequests) {
-            if (storedRequest instanceof PermissionSyncRequest) {
-                Slog.e(LOG_TAG, "The request has been sent before, you can not send "
-                        + "the same request type again.");
-                return null;
-            }
-        }
-
         Slog.i(LOG_TAG, "Creating permission sync intent for userId [" + userId
                 + "] associationId [" + associationId + "]");
 
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index b338d89..1363ef3 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -1046,18 +1046,30 @@
      */
     void showToastWhereUidIsRunning(int uid, String text, @Toast.Duration int duration,
             Looper looper) {
+        ArrayList<Integer> displayIdsForUid = getDisplayIdsWhereUidIsRunning(uid);
+        if (displayIdsForUid.isEmpty()) {
+            return;
+        }
+        DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+        for (int i = 0; i < displayIdsForUid.size(); i++) {
+            Display display = displayManager.getDisplay(displayIdsForUid.get(i));
+            if (display != null && display.isValid()) {
+                Toast.makeText(mContext.createDisplayContext(display), looper, text,
+                        duration).show();
+            }
+        }
+    }
+
+    private ArrayList<Integer> getDisplayIdsWhereUidIsRunning(int uid) {
+        ArrayList<Integer> displayIdsForUid = new ArrayList<>();
         synchronized (mVirtualDeviceLock) {
-            DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
             for (int i = 0; i < mVirtualDisplays.size(); i++) {
                 if (mVirtualDisplays.valueAt(i).getWindowPolicyController().containsUid(uid)) {
-                    Display display = displayManager.getDisplay(mVirtualDisplays.keyAt(i));
-                    if (display != null && display.isValid()) {
-                        Toast.makeText(mContext.createDisplayContext(display), looper, text,
-                                duration).show();
-                    }
+                    displayIdsForUid.add(mVirtualDisplays.keyAt(i));
                 }
             }
         }
+        return displayIdsForUid;
     }
 
     boolean isDisplayOwnedByVirtualDevice(int displayId) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 8fe61e7..78cbf2b 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -807,9 +807,9 @@
         ServiceRecord r = res.record;
         // Note, when startService() or startForegroundService() is called on an already
         // running SHORT_SERVICE FGS, the call will succeed (i.e. we won't throw
-        // ForegroundServiceStartNotAllowedException), even when the service is alerady timed
-        // out. This is because these APIs will essnetially only change the "started" state
-        // of the service, and it won't afect "the foreground-ness" of the service, or the type
+        // ForegroundServiceStartNotAllowedException), even when the service is already timed
+        // out. This is because these APIs will essentially only change the "started" state
+        // of the service, and it won't affect "the foreground-ness" of the service, or the type
         // of the FGS.
         // However, this call will still _not_ extend the SHORT_SERVICE timeout either.
         // Also, if the app tries to change the type of the FGS later (using
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 9079ba8..5dd0a3f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -16,6 +16,11 @@
 
 package com.android.server.am;
 
+import android.util.Log;
+import android.util.LogWriter;
+
+import java.io.PrintWriter;
+
 /**
  * Common class for the various debug {@link android.util.Log} output configuration in the activity
  * manager package.
@@ -38,6 +43,10 @@
     // Default log tag for the activity manager package.
     static final String TAG_AM = "ActivityManager";
 
+    // Default writer that emits "info" log events for the activity manager package.
+    static final PrintWriter LOG_WRITER_INFO = new PrintWriter(
+            new LogWriter(Log.INFO, TAG_AM));
+
     // Enable all debug log categories.
     static final boolean DEBUG_ALL = false;
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a3e5820..c25e579 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -135,6 +135,7 @@
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
+import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
 import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP;
@@ -13744,17 +13745,6 @@
                                 + "RECEIVER_NOT_EXPORTED flag");
             }
 
-            // STOPSHIP(b/259139792): Allow apps that are currently targeting U and in process of
-            // updating their receivers to be exempt from this requirement until their receivers
-            // are flagged.
-            if (requireExplicitFlagForDynamicReceivers) {
-                if ("com.shannon.imsservice".equals(callerPackage)) {
-                    // Note, a versionCode check for this package is not performed because this
-                    // package consumes the SecurityException, so it wouldn't be caught during
-                    // presubmit.
-                    requireExplicitFlagForDynamicReceivers = false;
-                }
-            }
             if (!onlyProtectedBroadcasts) {
                 if (receiver == null && !explicitExportStateDefined) {
                     // sticky broadcast, no flag specified (flag isn't required)
@@ -18723,27 +18713,25 @@
 
     @Override
     public void waitForBroadcastIdle() {
-        waitForBroadcastIdle(/* printWriter= */ null);
+        waitForBroadcastIdle(LOG_WRITER_INFO);
     }
 
-    public void waitForBroadcastIdle(@Nullable PrintWriter pw) {
+    public void waitForBroadcastIdle(@NonNull PrintWriter pw) {
         enforceCallingPermission(permission.DUMP, "waitForBroadcastIdle()");
         BroadcastLoopers.waitForIdle(pw);
         for (BroadcastQueue queue : mBroadcastQueues) {
             queue.waitForIdle(pw);
         }
-        if (pw != null) {
-            pw.println("All broadcast queues are idle!");
-            pw.flush();
-        }
+        pw.println("All broadcast queues are idle!");
+        pw.flush();
     }
 
     @Override
     public void waitForBroadcastBarrier() {
-        waitForBroadcastBarrier(/* printWriter= */ null, false, false);
+        waitForBroadcastBarrier(LOG_WRITER_INFO, false, false);
     }
 
-    public void waitForBroadcastBarrier(@Nullable PrintWriter pw,
+    public void waitForBroadcastBarrier(@NonNull PrintWriter pw,
             boolean flushBroadcastLoopers, boolean flushApplicationThreads) {
         enforceCallingPermission(permission.DUMP, "waitForBroadcastBarrier()");
         if (flushBroadcastLoopers) {
@@ -18761,11 +18749,7 @@
      * Wait for all pending {@link IApplicationThread} events to be processed in
      * all currently running apps.
      */
-    public void waitForApplicationBarrier(@Nullable PrintWriter pw) {
-        if (pw == null) {
-            pw = new PrintWriter(new LogWriter(Log.VERBOSE, TAG));
-        }
-
+    public void waitForApplicationBarrier(@NonNull PrintWriter pw) {
         final CountDownLatch finishedLatch = new CountDownLatch(1);
         final AtomicInteger pingCount = new AtomicInteger(0);
         final AtomicInteger pongCount = new AtomicInteger(0);
@@ -18779,6 +18763,7 @@
         // too quickly in parallel below
         pingCount.incrementAndGet();
 
+        synchronized (this) {
         synchronized (mProcLock) {
             final ArrayMap<String, SparseArray<ProcessRecord>> pmap =
                     mProcessList.getProcessNamesLOSP().getMap();
@@ -18803,6 +18788,7 @@
                 }
             }
         }
+        }
 
         // Now that we've dispatched all "ping" events above, we can send our
         // "pong" sentinel value
@@ -18813,15 +18799,18 @@
             try {
                 if (finishedLatch.await(1, TimeUnit.SECONDS)) {
                     pw.println("Finished application barriers!");
+                    pw.flush();
                     return;
                 } else {
                     pw.println("Waiting for application barriers, at " + pongCount.get() + " of "
                             + pingCount.get() + "...");
+                    pw.flush();
                 }
             } catch (InterruptedException ignored) {
             }
         }
         pw.println("Gave up waiting for application barriers!");
+        pw.flush();
     }
 
     void setIgnoreDeliveryGroupPolicy(@NonNull String broadcastAction) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 72e17d8..6c6ac1e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -38,6 +38,7 @@
 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
 import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE_COUNT;
@@ -112,6 +113,7 @@
 import android.util.ArraySet;
 import android.util.DebugUtils;
 import android.util.DisplayMetrics;
+import android.util.TeeWriter;
 import android.util.proto.ProtoOutputStream;
 import android.view.Display;
 import android.window.SplashScreen;
@@ -3364,11 +3366,13 @@
     }
 
     int runWaitForBroadcastIdle(PrintWriter pw) throws RemoteException {
+        pw = new PrintWriter(new TeeWriter(LOG_WRITER_INFO, pw));
         mInternal.waitForBroadcastIdle(pw);
         return 0;
     }
 
     int runWaitForBroadcastBarrier(PrintWriter pw) throws RemoteException {
+        pw = new PrintWriter(new TeeWriter(LOG_WRITER_INFO, pw));
         boolean flushBroadcastLoopers = false;
         boolean flushApplicationThreads = false;
         String opt;
@@ -3387,6 +3391,7 @@
     }
 
     int runWaitForApplicationBarrier(PrintWriter pw) throws RemoteException {
+        pw = new PrintWriter(new TeeWriter(LOG_WRITER_INFO, pw));
         mInternal.waitForApplicationBarrier(pw);
         return 0;
     }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index d9ba845..ed297d0 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -654,6 +654,7 @@
         synchronized (mLock) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             mHandler.post(() -> {
+                mCpuWakeupStats.onUidRemoved(uid);
                 synchronized (mStats) {
                     mStats.removeUidStatsLocked(uid, elapsedRealtime);
                 }
@@ -764,6 +765,7 @@
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
             mHandler.post(() -> {
+                mCpuWakeupStats.noteUidProcessState(uid, state);
                 synchronized (mStats) {
                     mStats.noteUidProcessStateLocked(uid, state, elapsedRealtime, uptime);
                 }
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index 4d46963..094b040 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -203,11 +203,11 @@
 
     /**
      * For {@link BroadcastQueueModernImpl}: Delay to apply to broadcasts
-     * targeting cached applications.
+     * targeting frozen applications.
      */
-    public long DELAY_CACHED_MILLIS = DEFAULT_DELAY_CACHED_MILLIS;
-    private static final String KEY_DELAY_CACHED_MILLIS = "bcast_delay_cached_millis";
-    private static final long DEFAULT_DELAY_CACHED_MILLIS = +120_000;
+    public long DELAY_FROZEN_MILLIS = DEFAULT_DELAY_FROZEN_MILLIS;
+    private static final String KEY_DELAY_FROZEN_MILLIS = "bcast_delay_frozen_millis";
+    private static final long DEFAULT_DELAY_FROZEN_MILLIS = +120_000;
 
     /**
      * For {@link BroadcastQueueModernImpl}: Delay to apply to urgent
@@ -373,8 +373,8 @@
                     DEFAULT_MAX_PENDING_BROADCASTS);
             DELAY_NORMAL_MILLIS = getDeviceConfigLong(KEY_DELAY_NORMAL_MILLIS,
                     DEFAULT_DELAY_NORMAL_MILLIS);
-            DELAY_CACHED_MILLIS = getDeviceConfigLong(KEY_DELAY_CACHED_MILLIS,
-                    DEFAULT_DELAY_CACHED_MILLIS);
+            DELAY_FROZEN_MILLIS = getDeviceConfigLong(KEY_DELAY_FROZEN_MILLIS,
+                    DEFAULT_DELAY_FROZEN_MILLIS);
             DELAY_URGENT_MILLIS = getDeviceConfigLong(KEY_DELAY_URGENT_MILLIS,
                     DEFAULT_DELAY_URGENT_MILLIS);
             MAX_HISTORY_COMPLETE_SIZE = getDeviceConfigInt(KEY_MAX_HISTORY_COMPLETE_SIZE,
@@ -421,8 +421,8 @@
             pw.print(KEY_MAX_PENDING_BROADCASTS, MAX_PENDING_BROADCASTS).println();
             pw.print(KEY_DELAY_NORMAL_MILLIS,
                     TimeUtils.formatDuration(DELAY_NORMAL_MILLIS)).println();
-            pw.print(KEY_DELAY_CACHED_MILLIS,
-                    TimeUtils.formatDuration(DELAY_CACHED_MILLIS)).println();
+            pw.print(KEY_DELAY_FROZEN_MILLIS,
+                    TimeUtils.formatDuration(DELAY_FROZEN_MILLIS)).println();
             pw.print(KEY_DELAY_URGENT_MILLIS,
                     TimeUtils.formatDuration(DELAY_URGENT_MILLIS)).println();
             pw.print(KEY_MAX_HISTORY_COMPLETE_SIZE, MAX_HISTORY_COMPLETE_SIZE).println();
diff --git a/services/core/java/com/android/server/am/BroadcastLoopers.java b/services/core/java/com/android/server/am/BroadcastLoopers.java
index a5535cb..92547ea 100644
--- a/services/core/java/com/android/server/am/BroadcastLoopers.java
+++ b/services/core/java/com/android/server/am/BroadcastLoopers.java
@@ -17,7 +17,6 @@
 package com.android.server.am;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -74,7 +73,7 @@
      * defined by {@link MessageQueue#isIdle()}. Note that {@link Message#when}
      * still in the future are ignored for the purposes of the idle test.
      */
-    public static void waitForIdle(@Nullable PrintWriter pw) {
+    public static void waitForIdle(@NonNull PrintWriter pw) {
         waitForCondition(pw, (looper, latch) -> {
             final MessageQueue queue = looper.getQueue();
             queue.addIdleHandler(() -> {
@@ -89,7 +88,7 @@
      * Note that {@link Message#when} still in the future are ignored for the purposes
      * of the idle test.
      */
-    public static void waitForBarrier(@Nullable PrintWriter pw) {
+    public static void waitForBarrier(@NonNull PrintWriter pw) {
         waitForCondition(pw, (looper, latch) -> {
             (new Handler(looper)).post(() -> {
                 latch.countDown();
@@ -100,7 +99,7 @@
     /**
      * Wait for all registered {@link Looper} instances to meet a certain condition.
      */
-    private static void waitForCondition(@Nullable PrintWriter pw,
+    private static void waitForCondition(@NonNull PrintWriter pw,
             @NonNull BiConsumer<Looper, CountDownLatch> condition) {
         final CountDownLatch latch;
         synchronized (sLoopers) {
@@ -122,18 +121,12 @@
             final long now = SystemClock.uptimeMillis();
             if (now >= lastPrint + 1000) {
                 lastPrint = now;
-                logv("Waiting for " + latch.getCount() + " loopers to drain...", pw);
+                pw.println("Waiting for " + latch.getCount() + " loopers to drain...");
+                pw.flush();
             }
             SystemClock.sleep(100);
         }
-        logv("Loopers drained!", pw);
-    }
-
-    private static void logv(@NonNull String msg, @Nullable PrintWriter pw) {
-        Slog.v(TAG, msg);
-        if (pw != null) {
-            pw.println(msg);
-            pw.flush();
-        }
+        pw.println("Loopers drained!");
+        pw.flush();
     }
 }
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 056e17a..fa3d4ca 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -21,6 +21,7 @@
 import static com.android.server.am.BroadcastRecord.isDeliveryStateTerminal;
 import static com.android.server.am.BroadcastRecord.isReceiverEquals;
 
+import android.annotation.CheckResult;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -188,7 +189,7 @@
     private @Reason int mRunnableAtReason = REASON_EMPTY;
     private boolean mRunnableAtInvalidated;
 
-    private boolean mProcessCached;
+    private boolean mUidFrozen;
     private boolean mProcessInstrumented;
     private boolean mProcessPersistent;
 
@@ -341,7 +342,12 @@
      * Predicates that choose to remove a broadcast <em>must</em> finish
      * delivery of the matched broadcast, to ensure that situations like ordered
      * broadcasts are handled consistently.
+     *
+     * @return if this operation may have changed internal state, indicating
+     *         that the caller is responsible for invoking
+     *         {@link BroadcastQueueModernImpl#updateRunnableList}
      */
+    @CheckResult
     public boolean forEachMatchingBroadcast(@NonNull BroadcastPredicate predicate,
             @NonNull BroadcastConsumer consumer, boolean andRemove) {
         boolean didSomething = false;
@@ -354,6 +360,7 @@
         return didSomething;
     }
 
+    @CheckResult
     private boolean forEachMatchingBroadcastInQueue(@NonNull ArrayDeque<SomeArgs> queue,
             @NonNull BroadcastPredicate predicate, @NonNull BroadcastConsumer consumer,
             boolean andRemove) {
@@ -370,6 +377,10 @@
                     args.recycle();
                     it.remove();
                     onBroadcastDequeued(record, recordIndex, recordWouldBeSkipped);
+                } else {
+                    // Even if we're leaving broadcast in queue, it may have
+                    // been mutated in such a way to change our runnable time
+                    invalidateRunnableAt();
                 }
                 didSomething = true;
             }
@@ -381,33 +392,43 @@
 
     /**
      * Update the actively running "warm" process for this process.
+     *
+     * @return if this operation may have changed internal state, indicating
+     *         that the caller is responsible for invoking
+     *         {@link BroadcastQueueModernImpl#updateRunnableList}
      */
-    public void setProcess(@Nullable ProcessRecord app) {
+    @CheckResult
+    public boolean setProcessAndUidFrozen(@Nullable ProcessRecord app, boolean uidFrozen) {
         this.app = app;
-        if (app != null) {
-            setProcessCached(app.isCached());
-            setProcessInstrumented(app.getActiveInstrumentation() != null);
-            setProcessPersistent(app.isPersistent());
-        } else {
-            setProcessCached(false);
-            setProcessInstrumented(false);
-            setProcessPersistent(false);
-        }
 
         // Since we may have just changed our PID, invalidate cached strings
         mCachedToString = null;
         mCachedToShortString = null;
+
+        boolean didSomething = false;
+        if (app != null) {
+            didSomething |= setProcessInstrumented(app.getActiveInstrumentation() != null);
+            didSomething |= setProcessPersistent(app.isPersistent());
+            didSomething |= setUidFrozen(uidFrozen);
+        } else {
+            didSomething |= setProcessInstrumented(false);
+            didSomething |= setProcessPersistent(false);
+            didSomething |= setUidFrozen(uidFrozen);
+        }
+        return didSomething;
     }
 
     /**
-     * Update if this process is in the "cached" state, typically signaling that
+     * Update if this UID is in the "frozen" state, typically signaling that
      * broadcast dispatch should be paused or delayed.
      */
-    @VisibleForTesting
-    void setProcessCached(boolean cached) {
-        if (mProcessCached != cached) {
-            mProcessCached = cached;
+    private boolean setUidFrozen(boolean frozen) {
+        if (mUidFrozen != frozen) {
+            mUidFrozen = frozen;
             invalidateRunnableAt();
+            return true;
+        } else {
+            return false;
         }
     }
 
@@ -416,10 +437,13 @@
      * signaling that broadcast dispatch should bypass all pauses or delays, to
      * avoid holding up test suites.
      */
-    public void setProcessInstrumented(boolean instrumented) {
+    private boolean setProcessInstrumented(boolean instrumented) {
         if (mProcessInstrumented != instrumented) {
             mProcessInstrumented = instrumented;
             invalidateRunnableAt();
+            return true;
+        } else {
+            return false;
         }
     }
 
@@ -427,10 +451,13 @@
      * Update if this process is in the "persistent" state, which signals broadcast dispatch should
      * bypass all pauses or delays to prevent the system from becoming out of sync with itself.
      */
-    public void setProcessPersistent(boolean persistent) {
+    private boolean setProcessPersistent(boolean persistent) {
         if (mProcessPersistent != persistent) {
             mProcessPersistent = persistent;
             invalidateRunnableAt();
+            return true;
+        } else {
+            return false;
         }
     }
 
@@ -650,8 +677,20 @@
         return mActive != null;
     }
 
-    void forceDelayBroadcastDelivery(long delayedDurationMs) {
-        mForcedDelayedDurationMs = delayedDurationMs;
+    /**
+     * @return if this operation may have changed internal state, indicating
+     *         that the caller is responsible for invoking
+     *         {@link BroadcastQueueModernImpl#updateRunnableList}
+     */
+    @CheckResult
+    boolean forceDelayBroadcastDelivery(long delayedDurationMs) {
+        if (mForcedDelayedDurationMs != delayedDurationMs) {
+            mForcedDelayedDurationMs = delayedDurationMs;
+            invalidateRunnableAt();
+            return true;
+        } else {
+            return false;
+        }
     }
 
     /**
@@ -723,10 +762,21 @@
      * broadcasts would be prioritized for dispatching, even if there are urgent broadcasts
      * waiting. This is typically used in case there are callers waiting for "barrier" to be
      * reached.
+     *
+     * @return if this operation may have changed internal state, indicating
+     *         that the caller is responsible for invoking
+     *         {@link BroadcastQueueModernImpl#updateRunnableList}
      */
+    @CheckResult
     @VisibleForTesting
-    void setPrioritizeEarliest(boolean prioritizeEarliest) {
-        mPrioritizeEarliest = prioritizeEarliest;
+    boolean setPrioritizeEarliest(boolean prioritizeEarliest) {
+        if (mPrioritizeEarliest != prioritizeEarliest) {
+            mPrioritizeEarliest = prioritizeEarliest;
+            invalidateRunnableAt();
+            return true;
+        } else {
+            return false;
+        }
     }
 
     /**
@@ -813,7 +863,7 @@
 
     public boolean isDeferredUntilActive() {
         if (mRunnableAtInvalidated) updateRunnableAt();
-        return mRunnableAtReason == BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER;
+        return mRunnableAtReason == BroadcastProcessQueue.REASON_INFINITE_DEFER;
     }
 
     public boolean hasDeferredBroadcasts() {
@@ -848,14 +898,14 @@
     }
 
     static final int REASON_EMPTY = 0;
-    static final int REASON_CACHED = 1;
+    static final int REASON_FROZEN = 1;
     static final int REASON_NORMAL = 2;
     static final int REASON_MAX_PENDING = 3;
     static final int REASON_BLOCKED = 4;
     static final int REASON_INSTRUMENTED = 5;
     static final int REASON_PERSISTENT = 6;
     static final int REASON_FORCE_DELAYED = 7;
-    static final int REASON_CACHED_INFINITE_DEFER = 8;
+    static final int REASON_INFINITE_DEFER = 8;
     static final int REASON_CONTAINS_FOREGROUND = 10;
     static final int REASON_CONTAINS_ORDERED = 11;
     static final int REASON_CONTAINS_ALARM = 12;
@@ -864,17 +914,18 @@
     static final int REASON_CONTAINS_RESULT_TO = 15;
     static final int REASON_CONTAINS_INSTRUMENTED = 16;
     static final int REASON_CONTAINS_MANIFEST = 17;
+    static final int REASON_FOREGROUND_ACTIVITIES = 18;
 
     @IntDef(flag = false, prefix = { "REASON_" }, value = {
             REASON_EMPTY,
-            REASON_CACHED,
+            REASON_FROZEN,
             REASON_NORMAL,
             REASON_MAX_PENDING,
             REASON_BLOCKED,
             REASON_INSTRUMENTED,
             REASON_PERSISTENT,
             REASON_FORCE_DELAYED,
-            REASON_CACHED_INFINITE_DEFER,
+            REASON_INFINITE_DEFER,
             REASON_CONTAINS_FOREGROUND,
             REASON_CONTAINS_ORDERED,
             REASON_CONTAINS_ALARM,
@@ -883,6 +934,7 @@
             REASON_CONTAINS_RESULT_TO,
             REASON_CONTAINS_INSTRUMENTED,
             REASON_CONTAINS_MANIFEST,
+            REASON_FOREGROUND_ACTIVITIES,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Reason {}
@@ -890,14 +942,14 @@
     static @NonNull String reasonToString(@Reason int reason) {
         switch (reason) {
             case REASON_EMPTY: return "EMPTY";
-            case REASON_CACHED: return "CACHED";
+            case REASON_FROZEN: return "FROZEN";
             case REASON_NORMAL: return "NORMAL";
             case REASON_MAX_PENDING: return "MAX_PENDING";
             case REASON_BLOCKED: return "BLOCKED";
             case REASON_INSTRUMENTED: return "INSTRUMENTED";
             case REASON_PERSISTENT: return "PERSISTENT";
             case REASON_FORCE_DELAYED: return "FORCE_DELAYED";
-            case REASON_CACHED_INFINITE_DEFER: return "INFINITE_DEFER";
+            case REASON_INFINITE_DEFER: return "INFINITE_DEFER";
             case REASON_CONTAINS_FOREGROUND: return "CONTAINS_FOREGROUND";
             case REASON_CONTAINS_ORDERED: return "CONTAINS_ORDERED";
             case REASON_CONTAINS_ALARM: return "CONTAINS_ALARM";
@@ -906,6 +958,7 @@
             case REASON_CONTAINS_RESULT_TO: return "CONTAINS_RESULT_TO";
             case REASON_CONTAINS_INSTRUMENTED: return "CONTAINS_INSTRUMENTED";
             case REASON_CONTAINS_MANIFEST: return "CONTAINS_MANIFEST";
+            case REASON_FOREGROUND_ACTIVITIES: return "FOREGROUND_ACTIVITIES";
             default: return Integer.toString(reason);
         }
     }
@@ -963,6 +1016,11 @@
             } else if (mProcessInstrumented) {
                 mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS;
                 mRunnableAtReason = REASON_INSTRUMENTED;
+            } else if (app != null && app.hasForegroundActivities()) {
+                // TODO: Listen for uid state changes to check when an uid goes in and out of
+                // the TOP state.
+                mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS;
+                mRunnableAtReason = REASON_FOREGROUND_ACTIVITIES;
             } else if (mCountOrdered > 0) {
                 mRunnableAt = runnableAt;
                 mRunnableAtReason = REASON_CONTAINS_ORDERED;
@@ -978,12 +1036,12 @@
             } else if (mProcessPersistent) {
                 mRunnableAt = runnableAt;
                 mRunnableAtReason = REASON_PERSISTENT;
-            } else if (mProcessCached) {
+            } else if (mUidFrozen) {
                 if (r.deferUntilActive) {
                     // All enqueued broadcasts are deferrable, defer
                     if (mCountDeferred == mCountEnqueued) {
                         mRunnableAt = Long.MAX_VALUE;
-                        mRunnableAtReason = REASON_CACHED_INFINITE_DEFER;
+                        mRunnableAtReason = REASON_INFINITE_DEFER;
                     } else {
                         // At least one enqueued broadcast isn't deferrable, repick time and reason
                         // for this record. If a later record is not deferrable and is one of these
@@ -998,14 +1056,14 @@
                             mRunnableAt = runnableAt;
                             mRunnableAtReason = REASON_CONTAINS_RESULT_TO;
                         } else {
-                            mRunnableAt = runnableAt + constants.DELAY_CACHED_MILLIS;
-                            mRunnableAtReason = REASON_CACHED;
+                            mRunnableAt = runnableAt + constants.DELAY_FROZEN_MILLIS;
+                            mRunnableAtReason = REASON_FROZEN;
                         }
                     }
                 } else {
                     // This record isn't deferrable
-                    mRunnableAt = runnableAt + constants.DELAY_CACHED_MILLIS;
-                    mRunnableAtReason = REASON_CACHED;
+                    mRunnableAt = runnableAt + constants.DELAY_FROZEN_MILLIS;
+                    mRunnableAtReason = REASON_FROZEN;
                 }
             } else if (mCountResultTo > 0) {
                 // All resultTo broadcasts are infinitely deferrable, so if the app
@@ -1187,8 +1245,8 @@
     @NeverCompile
     private void dumpProcessState(@NonNull IndentingPrintWriter pw) {
         final StringBuilder sb = new StringBuilder();
-        if (mProcessCached) {
-            sb.append("CACHED");
+        if (mUidFrozen) {
+            sb.append("FROZEN");
         }
         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 75e9336..6d1344d 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -69,14 +69,6 @@
         Slog.v(TAG, msg);
     }
 
-    static void logv(@NonNull String msg, @Nullable PrintWriter pw) {
-        logv(msg);
-        if (pw != null) {
-            pw.println(msg);
-            pw.flush();
-        }
-    }
-
     static void checkState(boolean expression, @NonNull String msg) {
         if (!expression) {
             throw new IllegalStateException(msg);
@@ -219,7 +211,7 @@
      * since running apps can continue sending new broadcasts in perpetuity;
      * consider using {@link #waitForBarrier} instead.
      */
-    public abstract void waitForIdle(@Nullable PrintWriter pw);
+    public abstract void waitForIdle(@NonNull PrintWriter pw);
 
     /**
      * Wait until any currently waiting broadcasts have been dispatched.
@@ -230,7 +222,7 @@
      * Callers are advised that this method will <em>not</em> wait for any
      * future broadcasts that are newly enqueued after being invoked.
      */
-    public abstract void waitForBarrier(@Nullable PrintWriter pw);
+    public abstract void waitForBarrier(@NonNull PrintWriter pw);
 
     /**
      * Delays delivering broadcasts to the specified package.
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index 4b8dc99..bd36c3f 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -835,7 +835,7 @@
                         OOM_ADJ_REASON_START_RECEIVER);
             }
         } else if (filter.receiverList.app != null) {
-            mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(filter.receiverList.app,
+            mService.mOomAdjuster.unfreezeTemporarily(filter.receiverList.app,
                     CachedAppOptimizer.UNFREEZE_REASON_START_RECEIVER);
         }
 
@@ -1129,7 +1129,7 @@
                     }
                     if (sendResult) {
                         if (r.callerApp != null) {
-                            mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(
+                            mService.mOomAdjuster.unfreezeTemporarily(
                                     r.callerApp,
                                     CachedAppOptimizer.UNFREEZE_REASON_FINISH_RECEIVER);
                         }
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 5010ec0..3a25a6e 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_FROZEN;
 import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
 import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
 
@@ -28,6 +29,7 @@
 import static com.android.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL;
 import static com.android.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_STOPPED;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
+import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO;
 import static com.android.server.am.BroadcastProcessQueue.insertIntoRunnableList;
 import static com.android.server.am.BroadcastProcessQueue.reasonToString;
 import static com.android.server.am.BroadcastProcessQueue.removeFromRunnableList;
@@ -47,7 +49,7 @@
 import android.app.ApplicationExitInfo;
 import android.app.BroadcastOptions;
 import android.app.IApplicationThread;
-import android.app.UidObserver;
+import android.app.IUidFrozenStateChangedCallback;
 import android.app.usage.UsageEvents.Event;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -71,6 +73,7 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
@@ -208,6 +211,16 @@
     private final AtomicReference<ArraySet<BroadcastRecord>> mReplacedBroadcastsCache =
             new AtomicReference<>();
 
+    /**
+     * Map from UID to its last known "frozen" 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 mUidFrozen = new SparseBooleanArray();
+
     private final BroadcastConstants mConstants;
     private final BroadcastConstants mFgConstants;
     private final BroadcastConstants mBgConstants;
@@ -348,8 +361,6 @@
         // If app isn't running, and there's nothing in the queue, clean up
         if (queue.isEmpty() && !queue.isActive() && !queue.isProcessWarm()) {
             removeProcessQueue(queue.processName, queue.uid);
-        } else {
-            updateQueueDeferred(queue);
         }
     }
 
@@ -484,7 +495,7 @@
         // relevant per-process queue
         final BroadcastProcessQueue queue = getProcessQueue(app);
         if (queue != null) {
-            queue.setProcess(app);
+            setQueueProcess(queue, app);
         }
 
         boolean didSomething = false;
@@ -525,7 +536,7 @@
         // relevant per-process queue
         final BroadcastProcessQueue queue = getProcessQueue(app);
         if (queue != null) {
-            queue.setProcess(null);
+            setQueueProcess(queue, null);
         }
 
         if ((mRunningColdStart != null) && (mRunningColdStart == queue)) {
@@ -552,6 +563,7 @@
                 return (r.receivers.get(i) instanceof BroadcastFilter);
             }, mBroadcastConsumerSkip, true);
             if (didSomething || queue.isEmpty()) {
+                updateQueueDeferred(queue);
                 updateRunnableList(queue);
                 enqueueUpdateRunningList();
             }
@@ -617,6 +629,7 @@
                 setDeliveryState(queue, null, r, i, receiver, BroadcastRecord.DELIVERY_DEFERRED,
                         "deferred at enqueue time");
             }
+            updateQueueDeferred(queue);
             updateRunnableList(queue);
             enqueueUpdateRunningList();
         }
@@ -927,7 +940,7 @@
         final ProcessRecord app = r.resultToApp;
         final IApplicationThread thread = (app != null) ? app.getOnewayThread() : null;
         if (thread != null) {
-            mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(
+            mService.mOomAdjuster.unfreezeTemporarily(
                     app, CachedAppOptimizer.UNFREEZE_REASON_FINISH_RECEIVER);
             if (r.shareIdentity && app.uid != r.callingUid) {
                 mService.mPackageManagerInt.grantImplicitAccess(r.userId, r.intent,
@@ -1065,6 +1078,7 @@
 
             final int queueIndex = getRunningIndexOf(queue);
             mRunning[queueIndex] = null;
+            updateQueueDeferred(queue);
             updateRunnableList(queue);
             enqueueUpdateRunningList();
 
@@ -1147,6 +1161,7 @@
                                 getReceiverUid(otherReceiver));
                         if (otherQueue != null) {
                             otherQueue.invalidateRunnableAt();
+                            updateQueueDeferred(otherQueue);
                             updateRunnableList(otherQueue);
                         }
                     }
@@ -1247,7 +1262,7 @@
      * the given {@link Predicate}.
      */
     private boolean testAllProcessQueues(@NonNull Predicate<BroadcastProcessQueue> test,
-            @NonNull String label, @Nullable PrintWriter pw) {
+            @NonNull String label, @NonNull PrintWriter pw) {
         for (int i = 0; i < mProcessQueues.size(); i++) {
             BroadcastProcessQueue leaf = mProcessQueues.valueAt(i);
             while (leaf != null) {
@@ -1255,14 +1270,16 @@
                     final long now = SystemClock.uptimeMillis();
                     if (now > mLastTestFailureTime + DateUtils.SECOND_IN_MILLIS) {
                         mLastTestFailureTime = now;
-                        logv("Test " + label + " failed due to " + leaf.toShortString(), pw);
+                        pw.println("Test " + label + " failed due to " + leaf.toShortString());
+                        pw.flush();
                     }
                     return false;
                 }
                 leaf = leaf.processNameNext;
             }
         }
-        logv("Test " + label + " passed", pw);
+        pw.println("Test " + label + " passed");
+        pw.flush();
         return true;
     }
 
@@ -1277,6 +1294,7 @@
                 if (queuePredicate.test(leaf)) {
                     if (leaf.forEachMatchingBroadcast(broadcastPredicate,
                             broadcastConsumer, andRemove)) {
+                        updateQueueDeferred(leaf);
                         updateRunnableList(leaf);
                         didSomething = true;
                     }
@@ -1290,29 +1308,40 @@
         return didSomething;
     }
 
-    private void forEachMatchingQueue(
+    private boolean forEachMatchingQueue(
             @NonNull Predicate<BroadcastProcessQueue> queuePredicate,
             @NonNull Consumer<BroadcastProcessQueue> queueConsumer) {
+        boolean didSomething = false;
         for (int i = mProcessQueues.size() - 1; i >= 0; i--) {
             BroadcastProcessQueue leaf = mProcessQueues.valueAt(i);
             while (leaf != null) {
                 if (queuePredicate.test(leaf)) {
                     queueConsumer.accept(leaf);
+                    updateQueueDeferred(leaf);
                     updateRunnableList(leaf);
+                    didSomething = true;
                 }
                 leaf = leaf.processNameNext;
             }
         }
+        if (didSomething) {
+            enqueueUpdateRunningList();
+        }
+        return didSomething;
     }
 
-    private void updateQueueDeferred(
-            @NonNull BroadcastProcessQueue leaf) {
+    @SuppressWarnings("CheckResult")
+    private void updateQueueDeferred(@NonNull BroadcastProcessQueue leaf) {
         if (leaf.isDeferredUntilActive()) {
+            // We ignore the returned value here since callers are invoking us
+            // just before updateRunnableList()
             leaf.forEachMatchingBroadcast((r, i) -> {
                 return r.deferUntilActive && (r.getDeliveryState(i)
                         == BroadcastRecord.DELIVERY_PENDING);
             }, mBroadcastConsumerDefer, false);
         } else if (leaf.hasDeferredBroadcasts()) {
+            // We ignore the returned value here since callers are invoking us
+            // just before updateRunnableList()
             leaf.forEachMatchingBroadcast((r, i) -> {
                 return r.deferUntilActive && (r.getDeliveryState(i)
                         == BroadcastRecord.DELIVERY_DEFERRED);
@@ -1325,23 +1354,31 @@
         mFgConstants.startObserving(mHandler, resolver);
         mBgConstants.startObserving(mHandler, resolver);
 
-        mService.registerUidObserver(new UidObserver() {
+        mService.registerUidFrozenStateChangedCallback(new IUidFrozenStateChangedCallback.Stub() {
             @Override
-            public void onUidCachedChanged(int uid, boolean cached) {
+            public void onUidFrozenStateChanged(int[] uids, int[] frozenStates) {
                 synchronized (mService) {
-                    BroadcastProcessQueue leaf = mProcessQueues.get(uid);
-                    while (leaf != null) {
-                        // Update internal state by refreshing values previously
-                        // read from any known running process
-                        leaf.setProcess(leaf.app);
-                        updateQueueDeferred(leaf);
-                        updateRunnableList(leaf);
-                        leaf = leaf.processNameNext;
+                    for (int i = 0; i < uids.length; i++) {
+                        final int uid = uids[i];
+                        final boolean frozen = frozenStates[i] == UID_FROZEN_STATE_FROZEN;
+                        if (frozen) {
+                            mUidFrozen.put(uid, true);
+                        } else {
+                            mUidFrozen.delete(uid);
+                        }
+
+                        BroadcastProcessQueue leaf = mProcessQueues.get(uid);
+                        while (leaf != null) {
+                            // Update internal state by refreshing values previously
+                            // read from any known running process
+                            setQueueProcess(leaf, leaf.app);
+                            leaf = leaf.processNameNext;
+                        }
+                        enqueueUpdateRunningList();
                     }
-                    enqueueUpdateRunningList();
                 }
             }
-        }, ActivityManager.UID_OBSERVER_CACHED, 0, "android");
+        });
 
         // Kick off periodic health checks
         mLocalHandler.sendEmptyMessage(MSG_CHECK_HEALTH);
@@ -1349,30 +1386,30 @@
 
     @Override
     public boolean isIdleLocked() {
-        return isIdleLocked(null);
+        return isIdleLocked(LOG_WRITER_INFO);
     }
 
-    public boolean isIdleLocked(@Nullable PrintWriter pw) {
+    public boolean isIdleLocked(@NonNull PrintWriter pw) {
         return testAllProcessQueues(q -> q.isIdle(), "idle", pw);
     }
 
     @Override
     public boolean isBeyondBarrierLocked(@UptimeMillisLong long barrierTime) {
-        return isBeyondBarrierLocked(barrierTime, null);
+        return isBeyondBarrierLocked(barrierTime, LOG_WRITER_INFO);
     }
 
     public boolean isBeyondBarrierLocked(@UptimeMillisLong long barrierTime,
-            @Nullable PrintWriter pw) {
+            @NonNull PrintWriter pw) {
         return testAllProcessQueues(q -> q.isBeyondBarrierLocked(barrierTime), "barrier", pw);
     }
 
     @Override
-    public void waitForIdle(@Nullable PrintWriter pw) {
+    public void waitForIdle(@NonNull PrintWriter pw) {
         waitFor(() -> isIdleLocked(pw));
     }
 
     @Override
-    public void waitForBarrier(@Nullable PrintWriter pw) {
+    public void waitForBarrier(@NonNull PrintWriter pw) {
         final long now = SystemClock.uptimeMillis();
         waitFor(() -> isBeyondBarrierLocked(now, pw));
     }
@@ -1495,7 +1532,20 @@
 
     private void updateWarmProcess(@NonNull BroadcastProcessQueue queue) {
         if (!queue.isProcessWarm()) {
-            queue.setProcess(mService.getProcessRecordLocked(queue.processName, queue.uid));
+            setQueueProcess(queue, mService.getProcessRecordLocked(queue.processName, queue.uid));
+        }
+    }
+
+    /**
+     * Update the {@link ProcessRecord} associated with the given
+     * {@link BroadcastProcessQueue}. Also updates any runnable status that
+     * might have changed as a side-effect.
+     */
+    private void setQueueProcess(@NonNull BroadcastProcessQueue queue,
+            @Nullable ProcessRecord app) {
+        if (queue.setProcessAndUidFrozen(app, mUidFrozen.get(queue.uid, false))) {
+            updateQueueDeferred(queue);
+            updateRunnableList(queue);
         }
     }
 
@@ -1513,7 +1563,7 @@
                 mService.updateLruProcessLocked(queue.app, false, null);
             }
 
-            mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(queue.app,
+            mService.mOomAdjuster.unfreezeTemporarily(queue.app,
                     CachedAppOptimizer.UNFREEZE_REASON_START_RECEIVER);
 
             if (queue.runningOomAdjusted) {
@@ -1689,7 +1739,7 @@
         }
 
         BroadcastProcessQueue created = new BroadcastProcessQueue(mConstants, processName, uid);
-        created.setProcess(mService.getProcessRecordLocked(processName, uid));
+        setQueueProcess(created, mService.getProcessRecordLocked(processName, uid));
 
         if (leaf == null) {
             mProcessQueues.put(uid, created);
@@ -1812,12 +1862,18 @@
         ipw.decreaseIndent();
         ipw.println();
 
-        ipw.println(" Broadcasts with ignored delivery group policies:");
+        ipw.println("Broadcasts with ignored delivery group policies:");
         ipw.increaseIndent();
         mService.dumpDeliveryGroupPolicyIgnoredActions(ipw);
         ipw.decreaseIndent();
         ipw.println();
 
+        ipw.println("Frozen UIDs:");
+        ipw.increaseIndent();
+        ipw.println(mUidFrozen.toString());
+        ipw.decreaseIndent();
+        ipw.println();
+
         if (dumpConstants) {
             mConstants.dump(ipw);
         }
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 6c9f602..110d69e 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -2124,7 +2124,7 @@
                 frozen = opt.isFrozen();
 
                 final UidRecord uidRec = proc.getUidRecord();
-                if (frozen && uidRec.areAllProcessesFrozen()) {
+                if (frozen && uidRec != null && uidRec.areAllProcessesFrozen()) {
                     uidRec.setFrozen(true);
                     mFreezeHandler.sendMessage(mFreezeHandler.obtainMessage(
                             UID_FROZEN_STATE_CHANGED_MSG, proc));
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 84a8099..a86c02d 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -349,6 +349,7 @@
     private final ArrayList<UidRecord> mTmpBecameIdle = new ArrayList<UidRecord>();
     private final ActiveUids mTmpUidRecords;
     private final ArrayDeque<ProcessRecord> mTmpQueue;
+    private final ArraySet<ProcessRecord> mTmpProcessSet = new ArraySet<>();
     private final ArraySet<ProcessRecord> mPendingProcessSet = new ArraySet<>();
     private final ArraySet<ProcessRecord> mProcessesInCycle = new ArraySet<>();
 
@@ -3472,4 +3473,29 @@
                     CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason));
         }
     }
+
+    @GuardedBy("mService")
+    void unfreezeTemporarily(ProcessRecord app, @OomAdjuster.OomAdjReason int reason) {
+        if (!mCachedAppOptimizer.useFreezer()) {
+            return;
+        }
+
+        final ProcessCachedOptimizerRecord opt = app.mOptRecord;
+        if (!opt.isFrozen() && !opt.isPendingFreeze()) {
+            return;
+        }
+
+        final ArrayList<ProcessRecord> processes = mTmpProcessList;
+        final ActiveUids uids = mTmpUidRecords;
+        mTmpProcessSet.add(app);
+        collectReachableProcessesLocked(mTmpProcessSet, processes, uids);
+        mTmpProcessSet.clear();
+        // Now processes contains app's downstream and app
+        final int size = processes.size();
+        for (int i = 0; i < size; i++) {
+            ProcessRecord proc = processes.get(i);
+            mCachedAppOptimizer.unfreezeTemporarily(proc, reason);
+        }
+        processes.clear();
+    }
 }
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 50d00b4..e651e23 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1050,6 +1050,11 @@
         return mState.isCached();
     }
 
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
+    public boolean hasForegroundActivities() {
+        return mState.hasForegroundActivities();
+    }
+
     boolean hasActivities() {
         return mWindowProcessController.hasActivities();
     }
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 965a07b..81ba4b8 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1548,19 +1548,20 @@
     }
 
     private void enforceGetAppOpsStatsPermissionIfNeeded(int uid, String packageName) {
-        final int callingUid = Binder.getCallingUid();
         // We get to access everything
-        if (callingUid == Process.myPid()) {
+        final int callingPid = Binder.getCallingPid();
+        if (callingPid == Process.myPid()) {
             return;
         }
         // Apps can access their own data
+        final int callingUid = Binder.getCallingUid();
         if (uid == callingUid && packageName != null
                 && checkPackage(uid, packageName) == MODE_ALLOWED) {
             return;
         }
         // Otherwise, you need a permission...
-        mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
-                Binder.getCallingPid(), callingUid, null);
+        mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS, callingPid,
+                callingUid, null);
     }
 
     /**
diff --git a/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java b/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
new file mode 100644
index 0000000..f0e4b0f5
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
@@ -0,0 +1,76 @@
+/*
+ * 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.inputmethod;
+
+import android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.InputMethodSubtypeHandle;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+final class HardwareKeyboardShortcutController {
+    @GuardedBy("ImfLock.class")
+    private final ArrayList<InputMethodSubtypeHandle> mSubtypeHandles = new ArrayList<>();
+
+    @GuardedBy("ImfLock.class")
+    void reset(@NonNull InputMethodUtils.InputMethodSettings settings) {
+        mSubtypeHandles.clear();
+        for (final InputMethodInfo imi : settings.getEnabledInputMethodListLocked()) {
+            if (!imi.shouldShowInInputMethodPicker()) {
+                continue;
+            }
+            final List<InputMethodSubtype> subtypes =
+                    settings.getEnabledInputMethodSubtypeListLocked(imi, true);
+            if (subtypes.isEmpty()) {
+                mSubtypeHandles.add(InputMethodSubtypeHandle.of(imi, null));
+            } else {
+                for (final InputMethodSubtype subtype : subtypes) {
+                    if (subtype.isSuitableForPhysicalKeyboardLayoutMapping()) {
+                        mSubtypeHandles.add(InputMethodSubtypeHandle.of(imi, subtype));
+                    }
+                }
+            }
+        }
+    }
+
+    @AnyThread
+    @Nullable
+    static <T> T getNeighborItem(@NonNull List<T> list, @NonNull T value, boolean next) {
+        final int size = list.size();
+        for (int i = 0; i < size; ++i) {
+            if (Objects.equals(value, list.get(i))) {
+                final int nextIndex = (i + (next ? 1 : -1) + size) % size;
+                return list.get(nextIndex);
+            }
+        }
+        return null;
+    }
+
+    @GuardedBy("ImfLock.class")
+    @Nullable
+    InputMethodSubtypeHandle onSubtypeSwitch(
+            @NonNull InputMethodSubtypeHandle currentImeAndSubtype, boolean forward) {
+        return getNeighborItem(mSubtypeHandles, currentImeAndSubtype, forward);
+    }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index b440208..2433211 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -316,6 +316,8 @@
     final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
     final ArrayMap<String, InputMethodInfo> mMethodMap = new ArrayMap<>();
     final InputMethodSubtypeSwitchingController mSwitchingController;
+    final HardwareKeyboardShortcutController mHardwareKeyboardShortcutController =
+            new HardwareKeyboardShortcutController();
 
     /**
      * Tracks how many times {@link #mMethodMap} was updated.
@@ -1731,6 +1733,7 @@
         AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, userId);
         mSwitchingController =
                 InputMethodSubtypeSwitchingController.createInstanceLocked(mSettings, context);
+        mHardwareKeyboardShortcutController.reset(mSettings);
         mMenuController = new InputMethodMenuController(this);
         mBindingController =
                 bindingControllerForTesting != null
@@ -3268,6 +3271,7 @@
         // TODO: Make sure that mSwitchingController and mSettings are sharing the
         // the same enabled IMEs list.
         mSwitchingController.resetCircularListLocked(mContext);
+        mHardwareKeyboardShortcutController.reset(mSettings);
 
         sendOnNavButtonFlagsChangedLocked();
     }
@@ -5293,6 +5297,7 @@
         // TODO: Make sure that mSwitchingController and mSettings are sharing the
         // the same enabled IMEs list.
         mSwitchingController.resetCircularListLocked(mContext);
+        mHardwareKeyboardShortcutController.reset(mSettings);
 
         sendOnNavButtonFlagsChangedLocked();
 
@@ -5827,10 +5832,37 @@
         @Override
         public void switchKeyboardLayout(int direction) {
             synchronized (ImfLock.class) {
-                if (direction > 0) {
-                    switchToNextInputMethodLocked(null /* token */, true /* onlyCurrentIme */);
-                } else {
-                    // TODO(b/258853866): Support backwards switching.
+                final InputMethodInfo currentImi = mMethodMap.get(getSelectedMethodIdLocked());
+                if (currentImi == null) {
+                    return;
+                }
+                final InputMethodSubtypeHandle currentSubtypeHandle =
+                        InputMethodSubtypeHandle.of(currentImi, mCurrentSubtype);
+                final InputMethodSubtypeHandle nextSubtypeHandle =
+                        mHardwareKeyboardShortcutController.onSubtypeSwitch(currentSubtypeHandle,
+                                direction > 0);
+                if (nextSubtypeHandle == null) {
+                    return;
+                }
+                final InputMethodInfo nextImi = mMethodMap.get(nextSubtypeHandle.getImeId());
+                if (nextImi == null) {
+                    return;
+                }
+
+                final int subtypeCount = nextImi.getSubtypeCount();
+                if (subtypeCount == 0) {
+                    if (nextSubtypeHandle.equals(InputMethodSubtypeHandle.of(nextImi, null))) {
+                        setInputMethodLocked(nextImi.getId(), NOT_A_SUBTYPE_ID);
+                    }
+                    return;
+                }
+
+                for (int i = 0; i < subtypeCount; ++i) {
+                    if (nextSubtypeHandle.equals(
+                            InputMethodSubtypeHandle.of(nextImi, nextImi.getSubtypeAt(i)))) {
+                        setInputMethodLocked(nextImi.getId(), i);
+                        return;
+                    }
                 }
             }
         }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 65dcec7..9ec9ff5 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -656,7 +656,6 @@
     private ConditionProviders mConditionProviders;
     private NotificationUsageStats mUsageStats;
     private boolean mLockScreenAllowSecureNotifications = true;
-    boolean mAllowFgsDismissal = false;
     boolean mSystemExemptFromDismissal = false;
 
     private static final int MY_UID = Process.myUid();
@@ -2581,19 +2580,9 @@
             for (String name : properties.getKeyset()) {
                 if (SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE.equals(name)) {
                     mAssistants.resetDefaultAssistantsIfNecessary();
-                } else if (SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED.equals(name)) {
-                    String value = properties.getString(name, null);
-                    if ("true".equals(value)) {
-                        mAllowFgsDismissal = true;
-                    } else if ("false".equals(value)) {
-                        mAllowFgsDismissal = false;
-                    }
                 }
             }
         };
-        mAllowFgsDismissal = DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED, true);
         mSystemExemptFromDismissal = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
                 /* name= */ "application_exemptions",
@@ -7736,9 +7725,6 @@
                     // flags are set.
                     if ((notification.flags & FLAG_FOREGROUND_SERVICE) != 0) {
                         notification.flags |= FLAG_NO_CLEAR;
-                        if (!mAllowFgsDismissal) {
-                            notification.flags |= FLAG_ONGOING_EVENT;
-                        }
                     }
 
                     mRankingHelper.extractSignals(r);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 927a722..ab9d1cf 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -278,6 +278,7 @@
     private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
 
     static final int WRITE_USER_MSG = 1;
+    static final int WRITE_USER_LIST_MSG = 2;
     static final int WRITE_USER_DELAY = 2*1000;  // 2 seconds
 
     private static final long BOOT_USER_SET_TIMEOUT_MS = 300_000;
@@ -321,7 +322,6 @@
     private final Handler mHandler;
 
     private final File mUsersDir;
-    @GuardedBy("mPackagesLock")
     private final File mUserListFile;
 
     private final IBinder mUserRestrictionToken = new Binder();
@@ -3623,77 +3623,95 @@
         mUpdatingSystemUserMode = true;
     }
 
+
+    private ResilientAtomicFile getUserListFile() {
+        File tempBackup = new File(mUserListFile.getParent(), mUserListFile.getName() + ".backup");
+        File reserveCopy = new File(mUserListFile.getParent(),
+                mUserListFile.getName() + ".reservecopy");
+        int fileMode = FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH;
+        return new ResilientAtomicFile(mUserListFile, tempBackup, reserveCopy, fileMode,
+                "user list", (priority, msg) -> {
+            Slog.e(LOG_TAG, msg);
+            // Something went wrong, schedule full rewrite.
+            scheduleWriteUserList();
+        });
+    }
+
     @GuardedBy({"mPackagesLock"})
     private void readUserListLP() {
-        if (!mUserListFile.exists()) {
-            fallbackToSingleUserLP();
-            return;
-        }
-        FileInputStream fis = null;
-        AtomicFile userListFile = new AtomicFile(mUserListFile);
-        try {
-            fis = userListFile.openRead();
-            final TypedXmlPullParser parser = Xml.resolvePullParser(fis);
-            int type;
-            while ((type = parser.next()) != XmlPullParser.START_TAG
-                    && type != XmlPullParser.END_DOCUMENT) {
-                // Skip
-            }
+        try (ResilientAtomicFile file = getUserListFile()) {
+            FileInputStream fin = null;
+            try {
+                fin = file.openRead();
+                if (fin == null) {
+                    Slog.e(LOG_TAG, "userlist.xml not found, fallback to single user");
+                    fallbackToSingleUserLP();
+                    return;
+                }
 
-            if (type != XmlPullParser.START_TAG) {
-                Slog.e(LOG_TAG, "Unable to read user list");
-                fallbackToSingleUserLP();
-                return;
-            }
+                final TypedXmlPullParser parser = Xml.resolvePullParser(fin);
+                int type;
+                while ((type = parser.next()) != XmlPullParser.START_TAG
+                        && type != XmlPullParser.END_DOCUMENT) {
+                    // Skip
+                }
 
-            mNextSerialNumber = -1;
-            if (parser.getName().equals(TAG_USERS)) {
-                mNextSerialNumber =
-                        parser.getAttributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber);
-                mUserVersion =
-                        parser.getAttributeInt(null, ATTR_USER_VERSION, mUserVersion);
-                mUserTypeVersion =
-                        parser.getAttributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion);
-            }
+                if (type != XmlPullParser.START_TAG) {
+                    Slog.e(LOG_TAG, "Unable to read user list");
+                    fallbackToSingleUserLP();
+                    return;
+                }
 
-            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
-                if (type == XmlPullParser.START_TAG) {
-                    final String name = parser.getName();
-                    if (name.equals(TAG_USER)) {
-                        UserData userData = readUserLP(parser.getAttributeInt(null, ATTR_ID));
+                mNextSerialNumber = -1;
+                if (parser.getName().equals(TAG_USERS)) {
+                    mNextSerialNumber =
+                            parser.getAttributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber);
+                    mUserVersion =
+                            parser.getAttributeInt(null, ATTR_USER_VERSION, mUserVersion);
+                    mUserTypeVersion =
+                            parser.getAttributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion);
+                }
 
-                        if (userData != null) {
-                            synchronized (mUsersLock) {
-                                mUsers.put(userData.info.id, userData);
-                                if (mNextSerialNumber < 0
-                                        || mNextSerialNumber <= userData.info.id) {
-                                    mNextSerialNumber = userData.info.id + 1;
-                                }
-                            }
-                        }
-                    } else if (name.equals(TAG_GUEST_RESTRICTIONS)) {
-                        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
-                                && type != XmlPullParser.END_TAG) {
-                            if (type == XmlPullParser.START_TAG) {
-                                if (parser.getName().equals(TAG_RESTRICTIONS)) {
-                                    synchronized (mGuestRestrictions) {
-                                        UserRestrictionsUtils
-                                                .readRestrictions(parser, mGuestRestrictions);
+                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+                    if (type == XmlPullParser.START_TAG) {
+                        final String name = parser.getName();
+                        if (name.equals(TAG_USER)) {
+                            UserData userData = readUserLP(parser.getAttributeInt(null, ATTR_ID));
+
+                            if (userData != null) {
+                                synchronized (mUsersLock) {
+                                    mUsers.put(userData.info.id, userData);
+                                    if (mNextSerialNumber < 0
+                                            || mNextSerialNumber <= userData.info.id) {
+                                        mNextSerialNumber = userData.info.id + 1;
                                     }
                                 }
-                                break;
+                            }
+                        } else if (name.equals(TAG_GUEST_RESTRICTIONS)) {
+                            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                                    && type != XmlPullParser.END_TAG) {
+                                if (type == XmlPullParser.START_TAG) {
+                                    if (parser.getName().equals(TAG_RESTRICTIONS)) {
+                                        synchronized (mGuestRestrictions) {
+                                            UserRestrictionsUtils
+                                                    .readRestrictions(parser, mGuestRestrictions);
+                                        }
+                                    }
+                                    break;
+                                }
                             }
                         }
                     }
                 }
-            }
 
-            updateUserIds();
-            upgradeIfNecessaryLP();
-        } catch (IOException | XmlPullParserException e) {
-            fallbackToSingleUserLP();
-        } finally {
-            IoUtils.closeQuietly(fis);
+                updateUserIds();
+                upgradeIfNecessaryLP();
+            } catch (Exception e) {
+                // Remove corrupted file and retry.
+                file.failRead(fin, e);
+                readUserListLP();
+                return;
+            }
         }
 
         synchronized (mUsersLock) {
@@ -4099,6 +4117,18 @@
         }
     }
 
+    private void scheduleWriteUserList() {
+        if (DBG) {
+            debug("scheduleWriteUserList");
+        }
+        // No need to wrap it within a lock -- worst case, we'll just post the same message
+        // twice.
+        if (!mHandler.hasMessages(WRITE_USER_LIST_MSG)) {
+            Message msg = mHandler.obtainMessage(WRITE_USER_LIST_MSG);
+            mHandler.sendMessageDelayed(msg, WRITE_USER_DELAY);
+        }
+    }
+
     private void scheduleWriteUser(UserData userData) {
         if (DBG) {
             debug("scheduleWriteUser");
@@ -4111,20 +4141,37 @@
         }
     }
 
+    private ResilientAtomicFile getUserFile(int userId) {
+        File file = new File(mUsersDir, userId + XML_SUFFIX);
+        File tempBackup = new File(mUsersDir, userId + XML_SUFFIX + ".backup");
+        File reserveCopy = new File(mUsersDir, userId + XML_SUFFIX + ".reservecopy");
+        int fileMode = FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH;
+        return new ResilientAtomicFile(file, tempBackup, reserveCopy, fileMode,
+                "user info", (priority, msg) -> {
+            Slog.e(LOG_TAG, msg);
+            // Something went wrong, schedule full rewrite.
+            UserData userData = getUserDataNoChecks(userId);
+            if (userData != null) {
+                scheduleWriteUser(userData);
+            }
+        });
+    }
+
     @GuardedBy({"mPackagesLock"})
     private void writeUserLP(UserData userData) {
         if (DBG) {
             debug("writeUserLP " + userData);
         }
-        FileOutputStream fos = null;
-        AtomicFile userFile = new AtomicFile(new File(mUsersDir, userData.info.id + XML_SUFFIX));
-        try {
-            fos = userFile.startWrite();
-            writeUserLP(userData, fos);
-            userFile.finishWrite(fos);
-        } catch (Exception ioe) {
-            Slog.e(LOG_TAG, "Error writing user info " + userData.info.id, ioe);
-            userFile.failWrite(fos);
+        try (ResilientAtomicFile userFile = getUserFile(userData.info.id)) {
+            FileOutputStream fos = null;
+            try {
+                fos = userFile.startWrite();
+                writeUserLP(userData, fos);
+                userFile.finishWrite(fos);
+            } catch (Exception ioe) {
+                Slog.e(LOG_TAG, "Error writing user info " + userData.info.id, ioe);
+                userFile.failWrite(fos);
+            }
         }
     }
 
@@ -4253,65 +4300,71 @@
         if (DBG) {
             debug("writeUserList");
         }
-        FileOutputStream fos = null;
-        AtomicFile userListFile = new AtomicFile(mUserListFile);
-        try {
-            fos = userListFile.startWrite();
-            final TypedXmlSerializer serializer = Xml.resolveSerializer(fos);
-            serializer.startDocument(null, true);
-            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
 
-            serializer.startTag(null, TAG_USERS);
-            serializer.attributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber);
-            serializer.attributeInt(null, ATTR_USER_VERSION, mUserVersion);
-            serializer.attributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion);
+        try (ResilientAtomicFile file = getUserListFile()) {
+            FileOutputStream fos = null;
+            try {
+                fos = file.startWrite();
 
-            serializer.startTag(null, TAG_GUEST_RESTRICTIONS);
-            synchronized (mGuestRestrictions) {
-                UserRestrictionsUtils
-                        .writeRestrictions(serializer, mGuestRestrictions, TAG_RESTRICTIONS);
-            }
-            serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
-            int[] userIdsToWrite;
-            synchronized (mUsersLock) {
-                userIdsToWrite = new int[mUsers.size()];
-                for (int i = 0; i < userIdsToWrite.length; i++) {
-                    UserInfo user = mUsers.valueAt(i).info;
-                    userIdsToWrite[i] = user.id;
+                final TypedXmlSerializer serializer = Xml.resolveSerializer(fos);
+                serializer.startDocument(null, true);
+                serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output",
+                        true);
+
+                serializer.startTag(null, TAG_USERS);
+                serializer.attributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber);
+                serializer.attributeInt(null, ATTR_USER_VERSION, mUserVersion);
+                serializer.attributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion);
+
+                serializer.startTag(null, TAG_GUEST_RESTRICTIONS);
+                synchronized (mGuestRestrictions) {
+                    UserRestrictionsUtils
+                            .writeRestrictions(serializer, mGuestRestrictions, TAG_RESTRICTIONS);
                 }
-            }
-            for (int id : userIdsToWrite) {
-                serializer.startTag(null, TAG_USER);
-                serializer.attributeInt(null, ATTR_ID, id);
-                serializer.endTag(null, TAG_USER);
-            }
+                serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
+                int[] userIdsToWrite;
+                synchronized (mUsersLock) {
+                    userIdsToWrite = new int[mUsers.size()];
+                    for (int i = 0; i < userIdsToWrite.length; i++) {
+                        UserInfo user = mUsers.valueAt(i).info;
+                        userIdsToWrite[i] = user.id;
+                    }
+                }
+                for (int id : userIdsToWrite) {
+                    serializer.startTag(null, TAG_USER);
+                    serializer.attributeInt(null, ATTR_ID, id);
+                    serializer.endTag(null, TAG_USER);
+                }
 
-            serializer.endTag(null, TAG_USERS);
+                serializer.endTag(null, TAG_USERS);
 
-            serializer.endDocument();
-            userListFile.finishWrite(fos);
-        } catch (Exception e) {
-            userListFile.failWrite(fos);
-            Slog.e(LOG_TAG, "Error writing user list");
+                serializer.endDocument();
+                file.finishWrite(fos);
+            } catch (Exception e) {
+                Slog.e(LOG_TAG, "Error writing user list", e);
+                file.failWrite(fos);
+            }
         }
     }
 
     @GuardedBy({"mPackagesLock"})
     private UserData readUserLP(int id) {
-        FileInputStream fis = null;
-        try {
-            AtomicFile userFile =
-                    new AtomicFile(new File(mUsersDir, Integer.toString(id) + XML_SUFFIX));
-            fis = userFile.openRead();
-            return readUserLP(id, fis);
-        } catch (IOException ioe) {
-            Slog.e(LOG_TAG, "Error reading user list");
-        } catch (XmlPullParserException pe) {
-            Slog.e(LOG_TAG, "Error reading user list");
-        } finally {
-            IoUtils.closeQuietly(fis);
+        try (ResilientAtomicFile file = getUserFile(id)) {
+            FileInputStream fis = null;
+            try {
+                fis = file.openRead();
+                if (fis == null) {
+                    Slog.e(LOG_TAG, "User info not found, returning null, user id: " + id);
+                    return null;
+                }
+                return readUserLP(id, fis);
+            } catch (Exception e) {
+                // Remove corrupted file and retry.
+                Slog.e(LOG_TAG, "Error reading user info, user id: " + id);
+                file.failRead(fis, e);
+                return readUserLP(id);
+            }
         }
-        return null;
     }
 
     @GuardedBy({"mPackagesLock"})
@@ -5805,9 +5858,8 @@
         synchronized (mPackagesLock) {
             writeUserListLP();
         }
-        // Remove user file
-        AtomicFile userFile = new AtomicFile(new File(mUsersDir, userId + XML_SUFFIX));
-        userFile.delete();
+        // Remove user file(s)
+        getUserFile(userId).delete();
         updateUserIds();
         if (RELEASE_DELETED_USER_ID) {
             synchronized (mUsersLock) {
@@ -6770,6 +6822,13 @@
         @Override
         public void handleMessage(Message msg) {
             switch (msg.what) {
+                case WRITE_USER_LIST_MSG: {
+                    removeMessages(WRITE_USER_LIST_MSG);
+                    synchronized (mPackagesLock) {
+                        writeUserListLP();
+                    }
+                    break;
+                }
                 case WRITE_USER_MSG:
                     removeMessages(WRITE_USER_MSG, msg.obj);
                     synchronized (mPackagesLock) {
@@ -6782,6 +6841,7 @@
                                     + ", it was probably removed before handler could handle it");
                         }
                     }
+                    break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index f340f93..c9ebeae 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -652,20 +652,33 @@
 
     private boolean isAdbVerificationEnabled(PackageInfoLite pkgInfoLite, int userId,
             boolean requestedDisableVerification) {
+        boolean verifierIncludeAdb = android.provider.Settings.Global.getInt(
+                mPm.mContext.getContentResolver(),
+                android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0;
+
         if (mPm.isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS)) {
+            if (!verifierIncludeAdb) {
+                Slog.w(TAG, "Force verification of ADB install because of user restriction.");
+            }
             return true;
         }
-        // Check if the developer wants to skip verification for ADB installs
+
+        // Check if the verification disabled globally, first.
+        if (!verifierIncludeAdb) {
+            return false;
+        }
+
+        // Check if the developer wants to skip verification for ADB installs.
         if (requestedDisableVerification) {
             if (!packageExists(pkgInfoLite.packageName)) {
-                // Always verify fresh install
+                // Always verify fresh install.
                 return true;
             }
-            // Only skip when apk is debuggable
+            // Only skip when apk is debuggable.
             return !pkgInfoLite.debuggable;
         }
-        return android.provider.Settings.Global.getInt(mPm.mContext.getContentResolver(),
-                android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0;
+
+        return true;
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index b56e5c9..c5f939a 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -64,6 +64,7 @@
 import android.permission.PermissionCheckerManager;
 import android.permission.PermissionManager;
 import android.permission.PermissionManagerInternal;
+import android.service.voice.VoiceInteractionManagerInternal;
 import android.util.ArrayMap;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -969,12 +970,13 @@
             // the private data in your process; or by you explicitly calling to another
             // app passing the source, in which case you must trust the other side;
 
-            final int callingUid = Binder.getCallingUid();
-            if (source.getUid() != callingUid && mContext.checkPermission(
+            final int callingUid = resolveUid(Binder.getCallingUid());
+            final int sourceUid = resolveUid(source.getUid());
+            if (sourceUid != callingUid && mContext.checkPermission(
                     Manifest.permission.UPDATE_APP_OPS_STATS, /*pid*/ -1, callingUid)
                     != PackageManager.PERMISSION_GRANTED) {
                 throw new SecurityException("Cannot register attribution source for uid:"
-                        + source.getUid() + " from uid:" + callingUid);
+                        + sourceUid + " from uid:" + callingUid);
             }
 
             final PackageManagerInternal packageManagerInternal = LocalServices.getService(
@@ -983,10 +985,10 @@
             // TODO(b/234653108): Clean up this UID/package & cross-user check.
             // If calling from the system process, allow registering attribution for package from
             // any user
-            int userId = UserHandle.getUserId((callingUid == Process.SYSTEM_UID ? source.getUid()
+            int userId = UserHandle.getUserId((callingUid == Process.SYSTEM_UID ? sourceUid
                     : callingUid));
             if (packageManagerInternal.getPackageUid(source.getPackageName(), 0, userId)
-                    != source.getUid()) {
+                    != sourceUid) {
                 throw new SecurityException("Cannot register attribution source for package:"
                         + source.getPackageName() + " from uid:" + callingUid);
             }
@@ -1012,6 +1014,21 @@
                 return false;
             }
         }
+
+        private int resolveUid(int uid) {
+            final VoiceInteractionManagerInternal vimi = LocalServices
+                    .getService(VoiceInteractionManagerInternal.class);
+            if (vimi == null) {
+                return uid;
+            }
+            final VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity
+                    hotwordDetectionServiceIdentity = vimi.getHotwordDetectionServiceIdentity();
+            if (hotwordDetectionServiceIdentity != null
+                    && uid == hotwordDetectionServiceIdentity.getIsolatedUid()) {
+                return hotwordDetectionServiceIdentity.getOwnerUid();
+            }
+            return uid;
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 54f87d0..8165958 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -729,7 +729,7 @@
                     mAutofillManagerInternal.onBackKeyPressed();
                     break;
                 case MSG_SYSTEM_KEY_PRESS:
-                    sendSystemKeyToStatusBar(msg.arg1);
+                    sendSystemKeyToStatusBar((KeyEvent) msg.obj);
                     break;
                 case MSG_HANDLE_ALL_APPS:
                     launchAllAppsAction();
@@ -949,7 +949,7 @@
         final boolean handledByPowerManager = mPowerManagerInternal.interceptPowerKeyDown(event);
 
         // Inform the StatusBar; but do not allow it to consume the event.
-        sendSystemKeyToStatusBarAsync(event.getKeyCode());
+        sendSystemKeyToStatusBarAsync(event);
 
         // If the power key has still not yet been handled, then detect short
         // press, long press, or multi press and decide what to do.
@@ -3001,7 +3001,11 @@
                 break;
             case KeyEvent.KEYCODE_N:
                 if (down && event.isMetaPressed()) {
-                    toggleNotificationPanel();
+                    if (event.isCtrlPressed()) {
+                        sendSystemKeyToStatusBarAsync(event);
+                    } else {
+                        toggleNotificationPanel();
+                    }
                     return key_consumed;
                 }
                 break;
@@ -3569,14 +3573,16 @@
 
     @Override
     public int applyKeyguardOcclusionChange() {
-        if (mKeyguardOccludedChanged) {
-            if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded changed occluded="
-                    + mPendingKeyguardOccluded);
-            if (setKeyguardOccludedLw(mPendingKeyguardOccluded)) {
-                return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_WALLPAPER;
-            }
+        if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded commit occluded="
+                + mPendingKeyguardOccluded);
+
+        // TODO(b/276433230): Explicitly save before/after for occlude state in each
+        // Transition so we don't need to update SysUI every time.
+        if (setKeyguardOccludedLw(mPendingKeyguardOccluded)) {
+            return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_WALLPAPER;
+        } else {
+            return 0;
         }
-        return 0;
     }
 
     /**
@@ -3854,6 +3860,7 @@
     private boolean setKeyguardOccludedLw(boolean isOccluded) {
         if (DEBUG_KEYGUARD) Slog.d(TAG, "setKeyguardOccluded occluded=" + isOccluded);
         mKeyguardOccludedChanged = false;
+        mPendingKeyguardOccluded = isOccluded;
         mKeyguardDelegate.setOccluded(isOccluded, true /* notify */);
         return mKeyguardDelegate.isShowing();
     }
@@ -4119,7 +4126,7 @@
             case KeyEvent.KEYCODE_VOLUME_UP:
             case KeyEvent.KEYCODE_VOLUME_MUTE: {
                 if (down) {
-                    sendSystemKeyToStatusBarAsync(event.getKeyCode());
+                    sendSystemKeyToStatusBarAsync(event);
 
                     NotificationManager nm = getNotificationService();
                     if (nm != null && !mHandleVolumeKeysInWM) {
@@ -4397,7 +4404,7 @@
             case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
             case KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL: {
                 if (down && mStylusButtonsEnabled) {
-                    sendSystemKeyToStatusBarAsync(keyCode);
+                    sendSystemKeyToStatusBarAsync(event);
                 }
                 result &= ~ACTION_PASS_TO_USER;
                 break;
@@ -4494,7 +4501,7 @@
             if (!mAccessibilityManager.isEnabled()
                     || !mAccessibilityManager.sendFingerprintGesture(event.getKeyCode())) {
                 if (mSystemNavigationKeysEnabled) {
-                    sendSystemKeyToStatusBarAsync(event.getKeyCode());
+                    sendSystemKeyToStatusBarAsync(event);
                 }
             }
         }
@@ -4503,11 +4510,11 @@
     /**
      * Notify the StatusBar that a system key was pressed.
      */
-    private void sendSystemKeyToStatusBar(int keyCode) {
+    private void sendSystemKeyToStatusBar(KeyEvent key) {
         IStatusBarService statusBar = getStatusBarService();
         if (statusBar != null) {
             try {
-                statusBar.handleSystemKey(keyCode);
+                statusBar.handleSystemKey(key);
             } catch (RemoteException e) {
                 // Oh well.
             }
@@ -4517,8 +4524,8 @@
     /**
      * Notify the StatusBar that a system key was pressed without blocking the current thread.
      */
-    private void sendSystemKeyToStatusBarAsync(int keyCode) {
-        Message message = mHandler.obtainMessage(MSG_SYSTEM_KEY_PRESS, keyCode, 0);
+    private void sendSystemKeyToStatusBarAsync(KeyEvent keyEvent) {
+        Message message = mHandler.obtainMessage(MSG_SYSTEM_KEY_PRESS, keyEvent);
         message.setAsynchronous(true);
         mHandler.sendMessage(message);
     }
diff --git a/services/core/java/com/android/server/power/stats/CpuWakeupStats.java b/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
index d55fbc2..231ffc6 100644
--- a/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
+++ b/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
@@ -20,6 +20,7 @@
 import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
 import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI;
 
+import android.app.ActivityManager;
 import android.content.Context;
 import android.os.Handler;
 import android.os.HandlerExecutor;
@@ -27,11 +28,10 @@
 import android.os.UserHandle;
 import android.provider.DeviceConfig;
 import android.util.IndentingPrintWriter;
-import android.util.IntArray;
-import android.util.LongSparseArray;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
 import android.util.SparseLongArray;
 import android.util.TimeSparseArray;
 import android.util.TimeUtils;
@@ -59,7 +59,7 @@
     private static final String TRACE_TRACK_WAKEUP_ATTRIBUTION = "wakeup_attribution";
     @VisibleForTesting
     static final long WAKEUP_REASON_HALF_WINDOW_MS = 500;
-    private static final long WAKEUP_WRITE_DELAY_MS = TimeUnit.MINUTES.toMillis(2);
+    private static final long WAKEUP_WRITE_DELAY_MS = TimeUnit.SECONDS.toMillis(30);
 
     private final Handler mHandler;
     private final IrqDeviceMap mIrqDeviceMap;
@@ -69,10 +69,15 @@
 
     @VisibleForTesting
     final TimeSparseArray<Wakeup> mWakeupEvents = new TimeSparseArray<>();
+
+    /* Maps timestamp -> {subsystem  -> {uid -> procState}} */
     @VisibleForTesting
-    final TimeSparseArray<SparseArray<SparseBooleanArray>> mWakeupAttribution =
+    final TimeSparseArray<SparseArray<SparseIntArray>> mWakeupAttribution =
             new TimeSparseArray<>();
 
+    final SparseIntArray mUidProcStates = new SparseIntArray();
+    private final SparseIntArray mReusableUidProcStates = new SparseIntArray(4);
+
     public CpuWakeupStats(Context context, int mapRes, Handler handler) {
         mIrqDeviceMap = IrqDeviceMap.getInstance(context, mapRes);
         mHandler = handler;
@@ -102,13 +107,14 @@
                     FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__TYPE__TYPE_UNKNOWN,
                     FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__UNKNOWN,
                     null,
-                    wakeupToLog.mElapsedMillis);
+                    wakeupToLog.mElapsedMillis,
+                    null);
             Trace.instantForTrack(Trace.TRACE_TAG_POWER, TRACE_TRACK_WAKEUP_ATTRIBUTION,
                     wakeupToLog.mElapsedMillis + " --");
             return;
         }
 
-        final SparseArray<SparseBooleanArray> wakeupAttribution = mWakeupAttribution.get(
+        final SparseArray<SparseIntArray> wakeupAttribution = mWakeupAttribution.get(
                 wakeupToLog.mElapsedMillis);
         if (wakeupAttribution == null) {
             // This is not expected but can theoretically happen in extreme situations, e.g. if we
@@ -121,24 +127,28 @@
 
         for (int i = 0; i < wakeupAttribution.size(); i++) {
             final int subsystem = wakeupAttribution.keyAt(i);
-            final SparseBooleanArray uidMap = wakeupAttribution.valueAt(i);
+            final SparseIntArray uidProcStates = wakeupAttribution.valueAt(i);
             final int[] uids;
-            if (uidMap == null || uidMap.size() == 0) {
-                uids = new int[0];
+            final int[] procStatesProto;
+
+            if (uidProcStates == null || uidProcStates.size() == 0) {
+                uids = procStatesProto = new int[0];
             } else {
-                final IntArray tmp = new IntArray(uidMap.size());
-                for (int j = 0; j < uidMap.size(); j++) {
-                    if (uidMap.valueAt(j)) {
-                        tmp.add(uidMap.keyAt(j));
-                    }
+                final int numUids = uidProcStates.size();
+                uids = new int[numUids];
+                procStatesProto = new int[numUids];
+                for (int j = 0; j < numUids; j++) {
+                    uids[j] = uidProcStates.keyAt(j);
+                    procStatesProto[j] = ActivityManager.processStateAmToProto(
+                            uidProcStates.valueAt(j));
                 }
-                uids = tmp.toArray();
             }
             FrameworkStatsLog.write(FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED,
                     FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__TYPE__TYPE_IRQ,
                     subsystemToStatsReason(subsystem),
                     uids,
-                    wakeupToLog.mElapsedMillis);
+                    wakeupToLog.mElapsedMillis,
+                    procStatesProto);
 
             if (Trace.isTagEnabled(Trace.TRACE_TAG_POWER)) {
                 if (i == 0) {
@@ -154,6 +164,20 @@
                 traceEventBuilder.toString().trim());
     }
 
+    /**
+     * Clean up data for a uid that is being removed.
+     */
+    public synchronized void onUidRemoved(int uid) {
+        mUidProcStates.delete(uid);
+    }
+
+    /**
+     * Notes a procstate change for the given uid to maintain the mapping internally.
+     */
+    public synchronized void noteUidProcessState(int uid, int state) {
+        mUidProcStates.put(uid, state);
+    }
+
     /** Notes a wakeup reason as reported by SuspendControlService to battery stats. */
     public synchronized void noteWakeupTimeAndReason(long elapsedRealtime, long uptime,
             String rawReason) {
@@ -184,8 +208,17 @@
 
     /** Notes a waking activity that could have potentially woken up the CPU. */
     public synchronized void noteWakingActivity(int subsystem, long elapsedRealtime, int... uids) {
-        if (!attemptAttributionWith(subsystem, elapsedRealtime, uids)) {
-            mRecentWakingActivity.recordActivity(subsystem, elapsedRealtime, uids);
+        if (uids == null) {
+            return;
+        }
+        mReusableUidProcStates.clear();
+        for (int i = 0; i < uids.length; i++) {
+            mReusableUidProcStates.put(uids[i],
+                    mUidProcStates.get(uids[i], ActivityManager.PROCESS_STATE_UNKNOWN));
+        }
+        if (!attemptAttributionWith(subsystem, elapsedRealtime, mReusableUidProcStates)) {
+            mRecentWakingActivity.recordActivity(subsystem, elapsedRealtime,
+                    mReusableUidProcStates);
         }
     }
 
@@ -196,7 +229,7 @@
             return;
         }
 
-        SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get(wakeup.mElapsedMillis);
+        SparseArray<SparseIntArray> attribution = mWakeupAttribution.get(wakeup.mElapsedMillis);
         if (attribution == null) {
             attribution = new SparseArray<>();
             mWakeupAttribution.put(wakeup.mElapsedMillis, attribution);
@@ -210,14 +243,14 @@
             final long startTime = wakeup.mElapsedMillis - WAKEUP_REASON_HALF_WINDOW_MS;
             final long endTime = wakeup.mElapsedMillis + WAKEUP_REASON_HALF_WINDOW_MS;
 
-            final SparseBooleanArray uidsToBlame = mRecentWakingActivity.removeBetween(subsystem,
+            final SparseIntArray uidsToBlame = mRecentWakingActivity.removeBetween(subsystem,
                     startTime, endTime);
             attribution.put(subsystem, uidsToBlame);
         }
     }
 
     private synchronized boolean attemptAttributionWith(int subsystem, long activityElapsed,
-            int... uids) {
+            SparseIntArray uidProcStates) {
         final int startIdx = mWakeupEvents.closestIndexOnOrAfter(
                 activityElapsed - WAKEUP_REASON_HALF_WINDOW_MS);
         final int endIdx = mWakeupEvents.closestIndexOnOrBefore(
@@ -233,19 +266,19 @@
             if (subsystems.get(subsystem)) {
                 // We don't expect more than one wakeup to be found within such a short window, so
                 // just attribute this one and exit
-                SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get(
+                SparseArray<SparseIntArray> attribution = mWakeupAttribution.get(
                         wakeup.mElapsedMillis);
                 if (attribution == null) {
                     attribution = new SparseArray<>();
                     mWakeupAttribution.put(wakeup.mElapsedMillis, attribution);
                 }
-                SparseBooleanArray uidsToBlame = attribution.get(subsystem);
+                SparseIntArray uidsToBlame = attribution.get(subsystem);
                 if (uidsToBlame == null) {
-                    uidsToBlame = new SparseBooleanArray(uids.length);
-                    attribution.put(subsystem, uidsToBlame);
-                }
-                for (final int uid : uids) {
-                    uidsToBlame.put(uid, true);
+                    attribution.put(subsystem, uidProcStates.clone());
+                } else {
+                    for (int i = 0; i < uidProcStates.size(); i++) {
+                        uidsToBlame.put(uidProcStates.keyAt(i), uidProcStates.valueAt(i));
+                    }
                 }
                 return true;
             }
@@ -267,6 +300,19 @@
         mRecentWakingActivity.dump(pw, nowElapsed);
         pw.println();
 
+        pw.println("Current proc-state map (" + mUidProcStates.size() + "):");
+        pw.increaseIndent();
+        for (int i = 0; i < mUidProcStates.size(); i++) {
+            if (i > 0) {
+                pw.print(", ");
+            }
+            UserHandle.formatUid(pw, mUidProcStates.keyAt(i));
+            pw.print(":" + ActivityManager.procStateToString(mUidProcStates.valueAt(i)));
+        }
+        pw.println();
+        pw.decreaseIndent();
+        pw.println();
+
         final SparseLongArray attributionStats = new SparseLongArray();
         pw.println("Wakeup events:");
         pw.increaseIndent();
@@ -278,7 +324,7 @@
             final Wakeup wakeup = mWakeupEvents.valueAt(i);
             pw.println(wakeup);
             pw.print("Attribution: ");
-            final SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get(
+            final SparseArray<SparseIntArray> attribution = mWakeupAttribution.get(
                     wakeup.mElapsedMillis);
             if (attribution == null) {
                 pw.println("N/A");
@@ -292,15 +338,17 @@
                     int attributed = IntPair.first(counters);
                     final int total = IntPair.second(counters) + 1;
 
-                    pw.print("subsystem: " + subsystemToString(attribution.keyAt(subsystemIdx)));
-                    pw.print(", uids: [");
-                    final SparseBooleanArray uids = attribution.valueAt(subsystemIdx);
-                    if (uids != null) {
-                        for (int uidIdx = 0; uidIdx < uids.size(); uidIdx++) {
+                    pw.print(subsystemToString(attribution.keyAt(subsystemIdx)));
+                    pw.print(" [");
+                    final SparseIntArray uidProcStates = attribution.valueAt(subsystemIdx);
+                    if (uidProcStates != null) {
+                        for (int uidIdx = 0; uidIdx < uidProcStates.size(); uidIdx++) {
                             if (uidIdx > 0) {
                                 pw.print(", ");
                             }
-                            UserHandle.formatUid(pw, uids.keyAt(uidIdx));
+                            UserHandle.formatUid(pw, uidProcStates.keyAt(uidIdx));
+                            pw.print(" " + ActivityManager.procStateToString(
+                                    uidProcStates.valueAt(uidIdx)));
                         }
                         attributed++;
                     }
@@ -330,29 +378,39 @@
         pw.println();
     }
 
+    /**
+     * This class stores recent unattributed activity history per subsystem.
+     * The activity is stored as a mapping of subsystem to timestamp to uid to procstate.
+     */
     private static final class WakingActivityHistory {
         private static final long WAKING_ACTIVITY_RETENTION_MS = TimeUnit.MINUTES.toMillis(10);
 
-        private SparseArray<TimeSparseArray<SparseBooleanArray>> mWakingActivity =
+        private SparseArray<TimeSparseArray<SparseIntArray>> mWakingActivity =
                 new SparseArray<>();
 
-        void recordActivity(int subsystem, long elapsedRealtime, int... uids) {
-            if (uids == null) {
+        void recordActivity(int subsystem, long elapsedRealtime, SparseIntArray uidProcStates) {
+            if (uidProcStates == null) {
                 return;
             }
-            TimeSparseArray<SparseBooleanArray> wakingActivity = mWakingActivity.get(subsystem);
+            TimeSparseArray<SparseIntArray> wakingActivity = mWakingActivity.get(subsystem);
             if (wakingActivity == null) {
                 wakingActivity = new TimeSparseArray<>();
                 mWakingActivity.put(subsystem, wakingActivity);
             }
-            SparseBooleanArray uidsToBlame = wakingActivity.get(elapsedRealtime);
+            final SparseIntArray uidsToBlame = wakingActivity.get(elapsedRealtime);
             if (uidsToBlame == null) {
-                uidsToBlame = new SparseBooleanArray(uids.length);
+                wakingActivity.put(elapsedRealtime, uidProcStates.clone());
+            } else {
+                for (int i = 0; i < uidProcStates.size(); i++) {
+                    final int uid = uidProcStates.keyAt(i);
+                    // Just in case there are duplicate uids reported with the same timestamp,
+                    // keep the processState which was reported first.
+                    if (uidsToBlame.indexOfKey(uid) < 0) {
+                        uidsToBlame.put(uid, uidProcStates.valueAt(i));
+                    }
+                }
                 wakingActivity.put(elapsedRealtime, uidsToBlame);
             }
-            for (int i = 0; i < uids.length; i++) {
-                uidsToBlame.put(uids[i], true);
-            }
             // Limit activity history per subsystem to the last WAKING_ACTIVITY_RETENTION_MS.
             // Note that the last activity is always present, even if it occurred before
             // WAKING_ACTIVITY_RETENTION_MS.
@@ -365,7 +423,7 @@
 
         void clearAllBefore(long elapsedRealtime) {
             for (int subsystemIdx = mWakingActivity.size() - 1; subsystemIdx >= 0; subsystemIdx--) {
-                final TimeSparseArray<SparseBooleanArray> activityPerSubsystem =
+                final TimeSparseArray<SparseIntArray> activityPerSubsystem =
                         mWakingActivity.valueAt(subsystemIdx);
                 final int endIdx = activityPerSubsystem.closestIndexOnOrBefore(elapsedRealtime);
                 for (int removeIdx = endIdx; removeIdx >= 0; removeIdx--) {
@@ -377,20 +435,20 @@
             }
         }
 
-        SparseBooleanArray removeBetween(int subsystem, long startElapsed, long endElapsed) {
-            final SparseBooleanArray uidsToReturn = new SparseBooleanArray();
+        SparseIntArray removeBetween(int subsystem, long startElapsed, long endElapsed) {
+            final SparseIntArray uidsToReturn = new SparseIntArray();
 
-            final TimeSparseArray<SparseBooleanArray> activityForSubsystem =
+            final TimeSparseArray<SparseIntArray> activityForSubsystem =
                     mWakingActivity.get(subsystem);
             if (activityForSubsystem != null) {
                 final int startIdx = activityForSubsystem.closestIndexOnOrAfter(startElapsed);
                 final int endIdx = activityForSubsystem.closestIndexOnOrBefore(endElapsed);
                 for (int i = endIdx; i >= startIdx; i--) {
-                    final SparseBooleanArray uidsForTime = activityForSubsystem.valueAt(i);
+                    final SparseIntArray uidsForTime = activityForSubsystem.valueAt(i);
                     for (int j = 0; j < uidsForTime.size(); j++) {
-                        if (uidsForTime.valueAt(j)) {
-                            uidsToReturn.put(uidsForTime.keyAt(j), true);
-                        }
+                        // In case the same uid appears in different uidsForTime maps, there is no
+                        // good way to choose one processState, so just arbitrarily pick any.
+                        uidsToReturn.put(uidsForTime.keyAt(j), uidsForTime.valueAt(j));
                     }
                 }
                 // More efficient to remove in a separate loop as it avoids repeatedly calling gc().
@@ -409,25 +467,23 @@
             pw.increaseIndent();
             for (int i = 0; i < mWakingActivity.size(); i++) {
                 pw.println("Subsystem " + subsystemToString(mWakingActivity.keyAt(i)) + ":");
-                final LongSparseArray<SparseBooleanArray> wakingActivity =
-                        mWakingActivity.valueAt(i);
+                final TimeSparseArray<SparseIntArray> wakingActivity = mWakingActivity.valueAt(i);
                 if (wakingActivity == null) {
                     continue;
                 }
                 pw.increaseIndent();
                 for (int j = wakingActivity.size() - 1; j >= 0; j--) {
                     TimeUtils.formatDuration(wakingActivity.keyAt(j), nowElapsed, pw);
-                    final SparseBooleanArray uidsToBlame = wakingActivity.valueAt(j);
+                    final SparseIntArray uidsToBlame = wakingActivity.valueAt(j);
                     if (uidsToBlame == null) {
                         pw.println();
                         continue;
                     }
                     pw.print(": ");
                     for (int k = 0; k < uidsToBlame.size(); k++) {
-                        if (uidsToBlame.valueAt(k)) {
-                            UserHandle.formatUid(pw, uidsToBlame.keyAt(k));
-                            pw.print(", ");
-                        }
+                        UserHandle.formatUid(pw, uidsToBlame.keyAt(k));
+                        pw.print(" [" + ActivityManager.procStateToString(uidsToBlame.valueAt(k)));
+                        pw.print("], ");
                     }
                     pw.println();
                 }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 35e88c1..363d2fd 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -84,6 +84,7 @@
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.view.KeyEvent;
 import android.view.WindowInsets;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowInsetsController.Appearance;
@@ -902,12 +903,12 @@
     }
 
     @Override
-    public void handleSystemKey(int key) throws RemoteException {
+    public void handleSystemKey(KeyEvent key) throws RemoteException {
         if (!checkCanCollapseStatusBar("handleSystemKey")) {
             return;
         }
 
-        mLastSystemKey = key;
+        mLastSystemKey = key.getKeyCode();
 
         if (mBar != null) {
             try {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 94e041a..5f56923 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -179,6 +179,9 @@
 import static com.android.server.wm.ActivityRecordProto.PROVIDES_MAX_BOUNDS;
 import static com.android.server.wm.ActivityRecordProto.REPORTED_DRAWN;
 import static com.android.server.wm.ActivityRecordProto.REPORTED_VISIBLE;
+import static com.android.server.wm.ActivityRecordProto.SHOULD_FORCE_ROTATE_FOR_CAMERA_COMPAT;
+import static com.android.server.wm.ActivityRecordProto.SHOULD_REFRESH_ACTIVITY_FOR_CAMERA_COMPAT;
+import static com.android.server.wm.ActivityRecordProto.SHOULD_REFRESH_ACTIVITY_VIA_PAUSE_FOR_CAMERA_COMPAT;
 import static com.android.server.wm.ActivityRecordProto.SHOULD_SEND_COMPAT_FAKE_FOCUS;
 import static com.android.server.wm.ActivityRecordProto.STARTING_DISPLAYED;
 import static com.android.server.wm.ActivityRecordProto.STARTING_MOVED;
@@ -10228,6 +10231,12 @@
         proto.write(LAST_DROP_INPUT_MODE, mLastDropInputMode);
         proto.write(OVERRIDE_ORIENTATION, getOverrideOrientation());
         proto.write(SHOULD_SEND_COMPAT_FAKE_FOCUS, shouldSendCompatFakeFocus());
+        proto.write(SHOULD_FORCE_ROTATE_FOR_CAMERA_COMPAT,
+                mLetterboxUiController.shouldForceRotateForCameraCompat());
+        proto.write(SHOULD_REFRESH_ACTIVITY_FOR_CAMERA_COMPAT,
+                mLetterboxUiController.shouldRefreshActivityForCameraCompat());
+        proto.write(SHOULD_REFRESH_ACTIVITY_VIA_PAUSE_FOR_CAMERA_COMPAT,
+                mLetterboxUiController.shouldRefreshActivityViaPauseForCameraCompat());
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 22dd0e5..d31fe23 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1981,6 +1981,14 @@
             return;
         }
 
+        if (controlTarget != null) {
+            final WindowState win = controlTarget.getWindow();
+
+            if (win != null && win.isActivityTypeDream()) {
+                return;
+            }
+        }
+
         final @InsetsType int restorePositionTypes = (Type.statusBars() | Type.navigationBars())
                 & controlTarget.getRequestedVisibleTypes();
 
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 052c09a..d65f464 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -99,23 +99,6 @@
         }
     }
 
-    WindowState getHostWindow(IBinder inputToken) {
-        EmbeddedWindow embeddedWindow = mWindows.get(inputToken);
-        return embeddedWindow != null ? embeddedWindow.mHostWindowState : null;
-    }
-
-    boolean isOverlay(IBinder inputToken) {
-        EmbeddedWindow embeddedWindow = mWindows.get(inputToken);
-        return embeddedWindow != null ? embeddedWindow.getIsOverlay() : false;
-    }
-
-    void setIsOverlay(IBinder focusGrantToken) {
-        EmbeddedWindow embeddedWindow = mWindowsByFocusToken.get(focusGrantToken);
-        if (embeddedWindow != null) {
-            embeddedWindow.setIsOverlay();
-        }
-    }
-
     void remove(IWindow client) {
         for (int i = mWindows.size() - 1; i >= 0; i--) {
             EmbeddedWindow ew = mWindows.valueAt(i);
@@ -176,14 +159,15 @@
         public Session mSession;
         InputChannel mInputChannel;
         final int mWindowType;
-        // Track whether the EmbeddedWindow is a system hosted overlay via
-        // {@link OverlayHost}. In the case of client hosted overlays, the client
-        // view hierarchy will take care of invoking requestEmbeddedWindowFocus
-        // but for system hosted overlays we have to do this via tapOutsideDetection
-        // and this variable is mostly used for tracking that.
-        boolean mIsOverlay = false;
 
-        private IBinder mFocusGrantToken;
+        /**
+         * A unique token associated with the embedded window that can be used by the host window
+         * to request focus transfer to the embedded. This is not the input token since we don't
+         * want to give clients access to each others input token.
+         */
+        private final IBinder mFocusGrantToken;
+
+        private boolean mIsFocusable;
 
         /**
          * @param session  calling session to check ownership of the window
@@ -199,7 +183,8 @@
          */
         EmbeddedWindow(Session session, WindowManagerService service, IWindow clientToken,
                        WindowState hostWindowState, int ownerUid, int ownerPid, int windowType,
-                       int displayId, IBinder focusGrantToken, String inputHandleName) {
+                       int displayId, IBinder focusGrantToken, String inputHandleName,
+                       boolean isFocusable) {
             mSession = session;
             mWmService = service;
             mClient = clientToken;
@@ -214,6 +199,7 @@
             final String hostWindowName =
                     (mHostWindowState != null) ? "-" + mHostWindowState.getWindowTag().toString()
                             : "";
+            mIsFocusable = isFocusable;
             mName = "Embedded{" + inputHandleName + hostWindowName + "}";
         }
 
@@ -279,13 +265,6 @@
             return mOwnerUid;
         }
 
-        void setIsOverlay() {
-            mIsOverlay = true;
-        }
-        boolean getIsOverlay() {
-            return mIsOverlay;
-        }
-
         IBinder getFocusGrantToken() {
             return mFocusGrantToken;
         }
@@ -297,20 +276,33 @@
             return null;
         }
 
+        void setIsFocusable(boolean isFocusable) {
+            mIsFocusable = isFocusable;
+        }
+
         /**
-         * System hosted overlays need the WM to invoke grantEmbeddedWindowFocus and
-         * so we need to participate inside handlePointerDownOutsideFocus logic
-         * however client hosted overlays will rely on the hosting view hierarchy
-         * to grant and revoke focus, and so the server side logic is not needed.
+         * When an embedded window is touched when it's not currently focus, we need to switch
+         * focus to that embedded window unless the embedded window was marked as not focusable.
          */
         @Override
         public boolean receiveFocusFromTapOutside() {
-            return mIsOverlay;
+            return mIsFocusable;
         }
 
         private void handleTap(boolean grantFocus) {
             if (mInputChannel != null) {
-                mWmService.grantEmbeddedWindowFocus(mSession, mFocusGrantToken, grantFocus);
+                if (mHostWindowState != null) {
+                    mWmService.grantEmbeddedWindowFocus(mSession, mHostWindowState.mClient,
+                            mFocusGrantToken, grantFocus);
+                    if (grantFocus) {
+                        // If granting focus to the embedded when tapped, we need to ensure the host
+                        // gains focus as well or the transfer won't take effect since it requires
+                        // the host to transfer the focus to the embedded.
+                        mHostWindowState.handleTapOutsideFocusInsideSelf();
+                    }
+                } else {
+                    mWmService.grantEmbeddedWindowFocus(mSession, mFocusGrantToken, grantFocus);
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
index 301c184..3d4e0eb 100644
--- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
+++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
@@ -289,6 +289,14 @@
         mChanged = true;
     }
 
+    void setFocusTransferTarget(IBinder toToken) {
+        if (mHandle.focusTransferTarget == toToken) {
+            return;
+        }
+        mHandle.focusTransferTarget = toToken;
+        mChanged = true;
+    }
+
     @Override
     public String toString() {
         return mHandle + ", changed=" + mChanged;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b7e2265..db44532 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4687,6 +4687,7 @@
         if (!isAttached()) {
             return;
         }
+        mTransitionController.collect(this);
 
         final TaskDisplayArea taskDisplayArea = getDisplayArea();
 
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index c6ba600..0fe1f92 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -50,6 +50,7 @@
 import static android.window.TransitionInfo.FLAG_IS_INPUT_METHOD;
 import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION;
 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
 import static android.window.TransitionInfo.FLAG_NO_ANIMATION;
 import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD;
 import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
@@ -183,6 +184,12 @@
     private final ArrayList<DisplayContent> mTargetDisplays = new ArrayList<>();
 
     /**
+     * The (non alwaysOnTop) tasks which were on-top of their display before the transition. If
+     * tasks are nested, all the tasks that are parents of the on-top task are also included.
+     */
+    private final ArrayList<Task> mOnTopTasksStart = new ArrayList<>();
+
+    /**
      * Set of participating windowtokens (activity/wallpaper) which are visible at the end of
      * the transition animation.
      */
@@ -515,6 +522,7 @@
         mParticipants.add(wc);
         if (wc.getDisplayContent() != null && !mTargetDisplays.contains(wc.getDisplayContent())) {
             mTargetDisplays.add(wc.getDisplayContent());
+            addOnTopTasks(wc.getDisplayContent(), mOnTopTasksStart);
         }
         if (info.mShowWallpaper) {
             // Collect the wallpaper token (for isWallpaper(wc)) so it is part of the sync set.
@@ -526,6 +534,27 @@
         }
     }
 
+    /** Adds the top non-alwaysOnTop tasks within `task` to `out`. */
+    private static void addOnTopTasks(Task task, ArrayList<Task> out) {
+        for (int i = task.getChildCount() - 1; i >= 0; --i) {
+            final Task child = task.getChildAt(i).asTask();
+            if (child == null) return;
+            if (child.getWindowConfiguration().isAlwaysOnTop()) continue;
+            out.add(child);
+            addOnTopTasks(child, out);
+            break;
+        }
+    }
+
+    /** Get the top non-alwaysOnTop leaf task on the display `dc`. */
+    private static void addOnTopTasks(DisplayContent dc, ArrayList<Task> out) {
+        final Task topNotAlwaysOnTop = dc.getRootTask(
+                t -> !t.getWindowConfiguration().isAlwaysOnTop());
+        if (topNotAlwaysOnTop == null) return;
+        out.add(topNotAlwaysOnTop);
+        addOnTopTasks(topNotAlwaysOnTop, out);
+    }
+
     /**
      * Records wc as changing its state of existence during this transition. For example, a new
      * task is considered an existence change while moving a task to front is not. wc is added
@@ -1000,11 +1029,13 @@
                 InsetsControlTarget prevImeTarget = dc.getImeTarget(
                         DisplayContent.IME_TARGET_CONTROL);
                 InsetsControlTarget newImeTarget = null;
+                TaskDisplayArea transientTDA = null;
                 // Transient-launch activities cannot be IME target (WindowState#canBeImeTarget),
                 // so re-compute in case the IME target is changed after transition.
                 for (int t = 0; t < mTransientLaunches.size(); ++t) {
                     if (mTransientLaunches.keyAt(t).getDisplayContent() == dc) {
                         newImeTarget = dc.computeImeTarget(true /* updateImeTarget */);
+                        transientTDA = mTransientLaunches.keyAt(i).getTaskDisplayArea();
                         break;
                     }
                 }
@@ -1015,10 +1046,17 @@
                     InputMethodManagerInternal.get().updateImeWindowStatus(
                             false /* disableImeIcon */);
                 }
+                // An uncommitted transient launch can leave incomplete lifecycles if visibilities
+                // didn't change (eg. re-ordering with translucent tasks will leave launcher
+                // in RESUMED state), so force an update here.
+                if (!hasVisibleTransientLaunch && transientTDA != null) {
+                    transientTDA.pauseBackTasks(null /* resuming */);
+                }
             }
             dc.removeImeSurfaceImmediately();
             dc.handleCompleteDeferredRemoval();
         }
+        validateKeyguardOcclusion();
         validateVisibility();
 
         mState = STATE_FINISHED;
@@ -1140,9 +1178,13 @@
         }
         // Check whether the participants were animated from back navigation.
         mController.mAtm.mBackNavigationController.onTransactionReady(this);
+
+        collectOrderChanges();
+
         // Resolve the animating targets from the participants.
         mTargets = calculateTargets(mParticipants, mChanges);
         final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, transaction);
+        info.setDebugId(mSyncId);
 
         // Repopulate the displays based on the resolved targets.
         mTargetDisplays.clear();
@@ -1175,8 +1217,6 @@
             if (mRecentsDisplayId != INVALID_DISPLAY) break;
         }
 
-        handleNonAppWindowsInTransition(mType, mFlags);
-
         // The callback is only populated for custom activity-level client animations
         sendRemoteCallback(mClientAnimationStartCallback);
 
@@ -1291,6 +1331,27 @@
         info.releaseAnimSurfaces();
     }
 
+    /** Collect tasks which moved-to-top but didn't change otherwise. */
+    @VisibleForTesting
+    void collectOrderChanges() {
+        if (mOnTopTasksStart.isEmpty()) return;
+        final ArrayList<Task> onTopTasksEnd = new ArrayList<>();
+        for (int i = 0; i < mTargetDisplays.size(); ++i) {
+            addOnTopTasks(mTargetDisplays.get(i), onTopTasksEnd);
+        }
+        for (int i = 0; i < onTopTasksEnd.size(); ++i) {
+            final Task task = onTopTasksEnd.get(i);
+            if (mOnTopTasksStart.contains(task)) continue;
+            mParticipants.add(task);
+            int changeIdx = mChanges.indexOfKey(task);
+            if (changeIdx < 0) {
+                mChanges.put(task, new ChangeInfo(task));
+                changeIdx = mChanges.indexOfKey(task);
+            }
+            mChanges.valueAt(changeIdx).mFlags |= ChangeInfo.FLAG_CHANGE_MOVED_TO_TOP;
+        }
+    }
+
     private void postCleanupOnFailure() {
         mController.mAtm.mH.post(() -> {
             synchronized (mController.mAtm.mGlobalLock) {
@@ -1480,19 +1541,6 @@
         }
     }
 
-    private void handleNonAppWindowsInTransition(
-            @TransitionType int transit, @TransitionFlags int flags) {
-        if ((flags & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
-            // If the occlusion changed but the transition isn't an occlude/unocclude transition,
-            // then we have to notify KeyguardService directly. This can happen if there is
-            // another ongoing transition when the app changes occlusion OR if the app dies or
-            // is killed. Both of these are common during tests.
-            if (transit != TRANSIT_KEYGUARD_OCCLUDE && transit != TRANSIT_KEYGUARD_UNOCCLUDE) {
-                mController.mAtm.mWindowManager.mPolicy.applyKeyguardOcclusionChange();
-            }
-        }
-    }
-
     private void reportStartReasonsToLogger() {
         // Record transition start in metrics logger. We just assume everything is "DRAWN"
         // at this point since splash-screen is a presentation (shell) detail.
@@ -2185,6 +2233,13 @@
         return mainWin.getAttrs().rotationAnimation;
     }
 
+    private void validateKeyguardOcclusion() {
+        if ((mFlags & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
+            mController.mStateValidators.add(
+                mController.mAtm.mWindowManager.mPolicy::applyKeyguardOcclusionChange);
+        }
+    }
+
     private void validateVisibility() {
         for (int i = mTargets.size() - 1; i >= 0; --i) {
             if (reduceMode(mTargets.get(i).mReadyMode) != TRANSIT_CLOSE) {
@@ -2246,13 +2301,17 @@
          */
         private static final int FLAG_CHANGE_YES_ANIMATION = 0x10;
 
+        /** Whether this change's container moved to the top. */
+        private static final int FLAG_CHANGE_MOVED_TO_TOP = 0x20;
+
         @IntDef(prefix = { "FLAG_" }, value = {
                 FLAG_NONE,
                 FLAG_SEAMLESS_ROTATION,
                 FLAG_TRANSIENT_LAUNCH,
                 FLAG_ABOVE_TRANSIENT_LAUNCH,
                 FLAG_CHANGE_NO_ANIMATION,
-                FLAG_CHANGE_YES_ANIMATION
+                FLAG_CHANGE_YES_ANIMATION,
+                FLAG_CHANGE_MOVED_TO_TOP
         })
         @Retention(RetentionPolicy.SOURCE)
         @interface Flag {}
@@ -2283,7 +2342,7 @@
         int mDisplayId = -1;
         @ActivityInfo.Config int mKnownConfigChanges;
 
-        /** These are just extra info. They aren't used for change-detection. */
+        /** Extra information about this change. */
         @Flag int mFlags = FLAG_NONE;
 
         /** Snapshot surface and luma, if relevant. */
@@ -2335,7 +2394,8 @@
                     || (mWindowingMode != 0 && mContainer.getWindowingMode() != mWindowingMode)
                     || !mContainer.getBounds().equals(mAbsoluteBounds)
                     || mRotation != mContainer.getWindowConfiguration().getRotation()
-                    || mDisplayId != getDisplayId(mContainer);
+                    || mDisplayId != getDisplayId(mContainer)
+                    || (mFlags & ChangeInfo.FLAG_CHANGE_MOVED_TO_TOP) != 0;
         }
 
         @TransitionInfo.TransitionMode
@@ -2436,6 +2496,9 @@
                     && (mFlags & FLAG_CHANGE_YES_ANIMATION) == 0) {
                 flags |= FLAG_NO_ANIMATION;
             }
+            if ((mFlags & FLAG_CHANGE_MOVED_TO_TOP) != 0) {
+                flags |= FLAG_MOVED_TO_TOP;
+            }
             return flags;
         }
 
diff --git a/services/core/java/com/android/server/wm/TrustedOverlayHost.java b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
index 88c410b..f8edc2b 100644
--- a/services/core/java/com/android/server/wm/TrustedOverlayHost.java
+++ b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
@@ -90,8 +90,6 @@
         requireOverlaySurfaceControl();
         mOverlays.add(p);
 
-        mWmService.mEmbeddedWindowController.setIsOverlay(p.getInputToken());
-
         SurfaceControl.Transaction t = mWmService.mTransactionFactory.get();
         t.reparent(p.getSurfaceControl(), mSurfaceControl)
             .show(p.getSurfaceControl());
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a7a9060..dde87b1 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8624,7 +8624,8 @@
             EmbeddedWindowController.EmbeddedWindow win =
                     new EmbeddedWindowController.EmbeddedWindow(session, this, window,
                             mInputToWindowMap.get(hostInputToken), callingUid, callingPid,
-                            sanitizedType, displayId, focusGrantToken, inputHandleName);
+                            sanitizedType, displayId, focusGrantToken, inputHandleName,
+                            (flags & FLAG_NOT_FOCUSABLE) == 0);
             clientChannel = win.openInputChannel();
             mEmbeddedWindowController.add(clientChannel.getToken(), win);
             applicationHandle = win.getApplicationHandle();
@@ -8745,6 +8746,7 @@
             }
             name = win.toString();
             applicationHandle = win.getApplicationHandle();
+            win.setIsFocusable((flags & FLAG_NOT_FOCUSABLE) == 0);
         }
 
         updateInputChannel(channelToken, win.mOwnerUid, win.mOwnerPid, displayId, surface, name,
@@ -9022,24 +9024,23 @@
                 Slog.e(TAG, "Embedded window does not belong to the host");
                 return;
             }
-            SurfaceControl.Transaction t = mTransactionFactory.get();
             if (grantFocus) {
-                t.requestFocusTransfer(embeddedWindow.getInputChannelToken(), embeddedWindow.toString(),
-                        hostWindow.mInputChannel.getToken(),
-                        hostWindow.getName(),
-                        hostWindow.getDisplayId()).apply();
+                hostWindow.mInputWindowHandle.setFocusTransferTarget(
+                        embeddedWindow.getInputChannelToken());
                 EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
                         "Transfer focus request " + embeddedWindow,
                         "reason=grantEmbeddedWindowFocus(true)");
             } else {
-                t.requestFocusTransfer(hostWindow.mInputChannel.getToken(), hostWindow.getName(),
-                        embeddedWindow.getInputChannelToken(),
-                        embeddedWindow.toString(),
-                        hostWindow.getDisplayId()).apply();
+                hostWindow.mInputWindowHandle.setFocusTransferTarget(null);
                 EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
                         "Transfer focus request " + hostWindow,
                         "reason=grantEmbeddedWindowFocus(false)");
             }
+            DisplayContent dc = mRoot.getDisplayContent(hostWindow.getDisplayId());
+            if (dc != null) {
+                dc.getInputMonitor().updateInputWindowsLw(true);
+            }
+
             ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s grantFocus=%s",
                     embeddedWindow, grantFocus);
         }
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 71acbb4..d64b5a1 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -136,7 +136,6 @@
     jmethodID getContextForDisplay;
     jmethodID notifyDropWindow;
     jmethodID getParentSurfaceForPointers;
-    jmethodID isPerDisplayTouchModeEnabled;
 } gServiceClassInfo;
 
 static struct {
@@ -369,10 +368,6 @@
     virtual PointerIconStyle getCustomPointerIconId();
     virtual void onPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position);
 
-    /* --- If touch mode is enabled per display or global --- */
-
-    virtual bool isPerDisplayTouchModeEnabled();
-
 private:
     sp<InputManagerInterface> mInputManager;
 
@@ -1645,16 +1640,6 @@
             InputReaderConfiguration::CHANGE_STYLUS_BUTTON_REPORTING);
 }
 
-bool NativeInputManager::isPerDisplayTouchModeEnabled() {
-    JNIEnv* env = jniEnv();
-    jboolean enabled =
-            env->CallBooleanMethod(mServiceObj, gServiceClassInfo.isPerDisplayTouchModeEnabled);
-    if (checkAndClearExceptionFromCallback(env, "isPerDisplayTouchModeEnabled")) {
-        return false;
-    }
-    return static_cast<bool>(enabled);
-}
-
 FloatPoint NativeInputManager::getMouseCursorPosition() {
     std::scoped_lock _l(mLock);
     const auto pc = mLocked.pointerController.lock();
@@ -2846,9 +2831,6 @@
     GET_METHOD_ID(gServiceClassInfo.getParentSurfaceForPointers, clazz,
                   "getParentSurfaceForPointers", "(I)J");
 
-    GET_METHOD_ID(gServiceClassInfo.isPerDisplayTouchModeEnabled, clazz,
-                  "isPerDisplayTouchModeEnabled", "()Z");
-
     // InputDevice
 
     FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice");
diff --git a/services/core/jni/gnss/AGnssRil.cpp b/services/core/jni/gnss/AGnssRil.cpp
index c7a1af7..b21489a 100644
--- a/services/core/jni/gnss/AGnssRil.cpp
+++ b/services/core/jni/gnss/AGnssRil.cpp
@@ -89,6 +89,10 @@
 }
 
 jboolean AGnssRil::injectNiSuplMessageData(const jbyteArray& msgData, jint length, jint slotIndex) {
+    if (mIAGnssRil->getInterfaceVersion() <= 2) {
+        ALOGE("IAGnssRil does not support injectNiSuplMessageData().");
+        return JNI_FALSE;
+    }
     JNIEnv* env = getJniEnv();
     jbyte* bytes = reinterpret_cast<jbyte*>(env->GetPrimitiveArrayCritical(msgData, 0));
     auto status = mIAGnssRil->injectNiSuplMessageData(std::vector<uint8_t>((const uint8_t*)bytes,
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/SwitchKeyboardLayoutTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/SwitchKeyboardLayoutTest.java
deleted file mode 100644
index 111cabd..0000000
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/SwitchKeyboardLayoutTest.java
+++ /dev/null
@@ -1,39 +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.server.inputmethod;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verify;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class SwitchKeyboardLayoutTest extends InputMethodManagerServiceTestBase {
-    @Test
-    public void testSwitchToNextKeyboardLayout() {
-        ExtendedMockito.spyOn(mInputMethodManagerService.mSwitchingController);
-        InputMethodManagerInternal.get().switchKeyboardLayout(1);
-        verify(mInputMethodManagerService.mSwitchingController)
-                .getNextInputMethodLocked(eq(true) /* onlyCurrentIme */, any(), any());
-    }
-}
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 36d191b..0ad0715 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -20,6 +20,7 @@
 import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED;
 import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD;
 import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST;
+import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO;
 import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_ALARM;
 import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_FOREGROUND;
 import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_INTERACTIVE;
@@ -129,7 +130,7 @@
         mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
         mConstants.DELAY_URGENT_MILLIS = -120_000;
         mConstants.DELAY_NORMAL_MILLIS = 10_000;
-        mConstants.DELAY_CACHED_MILLIS = 120_000;
+        mConstants.DELAY_FROZEN_MILLIS = 120_000;
 
         final BroadcastSkipPolicy emptySkipPolicy = new BroadcastSkipPolicy(mAms) {
             public boolean shouldSkip(BroadcastRecord r, Object o) {
@@ -371,13 +372,13 @@
                 List.of(makeMockRegisteredReceiver()), false);
         queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, false);
 
-        queue.setProcessCached(false);
+        queue.setProcessAndUidFrozen(null, false);
         final long notCachedRunnableAt = queue.getRunnableAt();
-        queue.setProcessCached(true);
+        queue.setProcessAndUidFrozen(null, true);
         final long cachedRunnableAt = queue.getRunnableAt();
         assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt);
         assertFalse(queue.isRunnable());
-        assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER,
+        assertEquals(BroadcastProcessQueue.REASON_INFINITE_DEFER,
                 queue.getRunnableAtReason());
         assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked());
     }
@@ -398,13 +399,13 @@
                 List.of(makeMockRegisteredReceiver()), false);
         queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, false);
 
-        queue.setProcessCached(false);
+        queue.setProcessAndUidFrozen(null, false);
         final long notCachedRunnableAt = queue.getRunnableAt();
-        queue.setProcessCached(true);
+        queue.setProcessAndUidFrozen(null, true);
         final long cachedRunnableAt = queue.getRunnableAt();
         assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt);
         assertTrue(queue.isRunnable());
-        assertEquals(BroadcastProcessQueue.REASON_CACHED, queue.getRunnableAtReason());
+        assertEquals(BroadcastProcessQueue.REASON_FROZEN, queue.getRunnableAtReason());
         assertEquals(ProcessList.SCHED_GROUP_BACKGROUND, queue.getPreferredSchedulingGroupLocked());
     }
 
@@ -430,13 +431,13 @@
         // verify that:
         // (a) the queue is immediately runnable by existence of a fg-priority broadcast
         // (b) the next one up is the fg-priority broadcast despite its later enqueue time
-        queue.setProcessCached(false);
+        queue.setProcessAndUidFrozen(null, false);
         assertTrue(queue.isRunnable());
         assertThat(queue.getRunnableAt()).isAtMost(airplaneRecord.enqueueClockTime);
         assertEquals(ProcessList.SCHED_GROUP_DEFAULT, queue.getPreferredSchedulingGroupLocked());
         assertEquals(queue.peekNextBroadcastRecord(), airplaneRecord);
 
-        queue.setProcessCached(true);
+        queue.setProcessAndUidFrozen(null, true);
         assertTrue(queue.isRunnable());
         assertThat(queue.getRunnableAt()).isAtMost(airplaneRecord.enqueueClockTime);
         assertEquals(ProcessList.SCHED_GROUP_DEFAULT, queue.getPreferredSchedulingGroupLocked());
@@ -496,10 +497,10 @@
      * Verify that a cached process that would normally be delayed becomes
      * immediately runnable when the given broadcast is enqueued.
      */
-    private void doRunnableAt_Cached(BroadcastRecord testRecord, int testRunnableAtReason) {
+    private void doRunnableAt_Frozen(BroadcastRecord testRecord, int testRunnableAtReason) {
         final BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants,
                 PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN));
-        queue.setProcessCached(true);
+        queue.setProcessAndUidFrozen(null, true);
 
         final BroadcastRecord lazyRecord = makeBroadcastRecord(
                 new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED),
@@ -515,49 +516,49 @@
     }
 
     @Test
-    public void testRunnableAt_Cached_Manifest() {
-        doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), null,
+    public void testRunnableAt_Frozen_Manifest() {
+        doRunnableAt_Frozen(makeBroadcastRecord(makeMockIntent(), null,
                 List.of(makeMockManifestReceiver()), null, false), REASON_CONTAINS_MANIFEST);
     }
 
     @Test
-    public void testRunnableAt_Cached_Ordered() {
-        doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), null,
+    public void testRunnableAt_Frozen_Ordered() {
+        doRunnableAt_Frozen(makeBroadcastRecord(makeMockIntent(), null,
                 List.of(makeMockRegisteredReceiver()), null, true), REASON_CONTAINS_ORDERED);
     }
 
     @Test
-    public void testRunnableAt_Cached_Foreground() {
+    public void testRunnableAt_Frozen_Foreground() {
         final Intent foregroundIntent = new Intent();
         foregroundIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        doRunnableAt_Cached(makeBroadcastRecord(foregroundIntent, null,
+        doRunnableAt_Frozen(makeBroadcastRecord(foregroundIntent, null,
                 List.of(makeMockRegisteredReceiver()), null, false), REASON_CONTAINS_FOREGROUND);
     }
 
     @Test
-    public void testRunnableAt_Cached_Interactive() {
+    public void testRunnableAt_Frozen_Interactive() {
         final BroadcastOptions options = BroadcastOptions.makeBasic();
         options.setInteractive(true);
-        doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options,
+        doRunnableAt_Frozen(makeBroadcastRecord(makeMockIntent(), options,
                 List.of(makeMockRegisteredReceiver()), null, false), REASON_CONTAINS_INTERACTIVE);
     }
 
     @Test
-    public void testRunnableAt_Cached_Alarm() {
+    public void testRunnableAt_Frozen_Alarm() {
         final BroadcastOptions options = BroadcastOptions.makeBasic();
         options.setAlarmBroadcast(true);
-        doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options,
+        doRunnableAt_Frozen(makeBroadcastRecord(makeMockIntent(), options,
                 List.of(makeMockRegisteredReceiver()), null, false), REASON_CONTAINS_ALARM);
     }
 
     @Test
-    public void testRunnableAt_Cached_Prioritized_NonDeferrable() {
+    public void testRunnableAt_Frozen_Prioritized_NonDeferrable() {
         final List receivers = List.of(
                 withPriority(makeManifestReceiver(PACKAGE_RED, PACKAGE_RED), 10),
                 withPriority(makeManifestReceiver(PACKAGE_GREEN, PACKAGE_GREEN), -10));
         final BroadcastOptions options = BroadcastOptions.makeBasic()
                 .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_NONE);
-        doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options,
+        doRunnableAt_Frozen(makeBroadcastRecord(makeMockIntent(), options,
                 receivers, null, false), REASON_CONTAINS_PRIORITIZED);
     }
 
@@ -1141,7 +1142,7 @@
             mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
             mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
         }
-        mImpl.waitForIdle(null);
+        mImpl.waitForIdle(LOG_WRITER_INFO);
 
         // Verify that there is only one delivery event reported since one of the broadcasts
         // should have been skipped.
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 bca39ae..73f645a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -16,8 +16,11 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_FROZEN;
+import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_UNFROZEN;
 import static android.os.UserHandle.USER_SYSTEM;
 
+import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO;
 import static com.android.server.am.BroadcastProcessQueue.reasonToString;
 import static com.android.server.am.BroadcastRecord.deliveryStateToString;
 import static com.android.server.am.BroadcastRecord.isReceiverEquals;
@@ -53,7 +56,7 @@
 import android.app.BackgroundStartPrivileges;
 import android.app.BroadcastOptions;
 import android.app.IApplicationThread;
-import android.app.UidObserver;
+import android.app.IUidFrozenStateChangedCallback;
 import android.app.usage.UsageEvents.Event;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.ComponentName;
@@ -162,7 +165,7 @@
     private BroadcastQueue mQueue;
     BroadcastConstants mConstants;
     private BroadcastSkipPolicy mSkipPolicy;
-    private UidObserver mUidObserver;
+    private IUidFrozenStateChangedCallback mUidFrozenStateChangedCallback;
 
     /**
      * Desired behavior of the next
@@ -222,7 +225,7 @@
         realAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
         realAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
         realAms.mAtmInternal = spy(realAms.mActivityTaskManager.getAtmInternal());
-        realAms.mOomAdjuster.mCachedAppOptimizer = spy(realAms.mOomAdjuster.mCachedAppOptimizer);
+        realAms.mOomAdjuster = spy(realAms.mOomAdjuster);
         realAms.mPackageManagerInt = mPackageManagerInt;
         realAms.mUsageStatsService = mUsageStatsManagerInt;
         realAms.mProcessesReady = true;
@@ -289,9 +292,9 @@
         doNothing().when(mAms).appNotResponding(any(), any());
 
         doAnswer((invocation) -> {
-            mUidObserver = invocation.getArgument(0);
+            mUidFrozenStateChangedCallback = invocation.getArgument(0);
             return null;
-        }).when(mAms).registerUidObserver(any(), anyInt(), anyInt(), any());
+        }).when(mAms).registerUidFrozenStateChangedCallback(any());
 
         mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
         mConstants.TIMEOUT = 100;
@@ -399,6 +402,18 @@
         DEAD,
     }
 
+    private void freezeUid(int uid) throws Exception {
+        mUidFrozenStateChangedCallback.onUidFrozenStateChanged(
+                new int[] { uid },
+                new int[] { UID_FROZEN_STATE_FROZEN });
+    }
+
+    private void thawUid(int uid) throws Exception {
+        mUidFrozenStateChangedCallback.onUidFrozenStateChanged(
+                new int[] { uid },
+                new int[] { UID_FROZEN_STATE_UNFROZEN });
+    }
+
     private ProcessRecord makeActiveProcessRecord(String packageName) throws Exception {
         return makeActiveProcessRecord(packageName, packageName, ProcessBehavior.NORMAL,
                 UserHandle.USER_SYSTEM);
@@ -659,7 +674,7 @@
     }
 
     private void waitForIdle() throws Exception {
-        mQueue.waitForIdle(null);
+        mQueue.waitForIdle(LOG_WRITER_INFO);
     }
 
     private void verifyScheduleReceiver(ProcessRecord app, Intent intent) throws Exception {
@@ -773,9 +788,6 @@
         mQueue.dumpToDropBoxLocked(TAG);
 
         BroadcastQueue.logv(TAG);
-        BroadcastQueue.logv(TAG, null);
-        BroadcastQueue.logv(TAG, new PrintWriter(new ByteArrayOutputStream()));
-
         BroadcastQueue.logw(TAG);
 
         assertNotNull(mQueue.toString());
@@ -951,7 +963,7 @@
                 // cold-started apps to be thawed, but the modern stack does
             } else {
                 // Confirm that app was thawed
-                verify(mAms.mOomAdjuster.mCachedAppOptimizer, atLeastOnce()).unfreezeTemporarily(
+                verify(mAms.mOomAdjuster, atLeastOnce()).unfreezeTemporarily(
                         eq(receiverApp), eq(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER));
 
                 // Confirm that we added package to process
@@ -1394,7 +1406,7 @@
                 anyInt(), any());
 
         // Finally, verify that we thawed the final receiver
-        verify(mAms.mOomAdjuster.mCachedAppOptimizer).unfreezeTemporarily(eq(callerApp),
+        verify(mAms.mOomAdjuster).unfreezeTemporarily(eq(callerApp),
                 eq(OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER));
     }
 
@@ -1661,8 +1673,8 @@
         final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW);
         final ProcessRecord receiverOrangeApp = makeActiveProcessRecord(PACKAGE_ORANGE);
 
-        receiverGreenApp.setCached(true);
-        receiverBlueApp.setCached(true);
+        freezeUid(getUidForPackage(PACKAGE_GREEN));
+        freezeUid(getUidForPackage(PACKAGE_BLUE));
 
         final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
         final BroadcastOptions opts = BroadcastOptions.makeBasic()
@@ -1706,14 +1718,12 @@
                 eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
 
         // Shift blue to be active and confirm that deferred broadcast is delivered
-        receiverBlueApp.setCached(false);
-        mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_BLUE), false);
+        thawUid(getUidForPackage(PACKAGE_BLUE));
         waitForIdle();
         verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, timeTick);
 
         // Shift green to be active and confirm that deferred broadcast is delivered
-        receiverGreenApp.setCached(false);
-        mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), false);
+        thawUid(getUidForPackage(PACKAGE_GREEN));
         waitForIdle();
         verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, timeTick);
     }
@@ -1891,10 +1901,10 @@
             assertFalse(mQueue.isBeyondBarrierLocked(afterSecond));
         }
 
-        mQueue.waitForBarrier(null);
+        mQueue.waitForBarrier(LOG_WRITER_INFO);
         assertTrue(mQueue.isBeyondBarrierLocked(afterFirst));
 
-        mQueue.waitForIdle(null);
+        mQueue.waitForIdle(LOG_WRITER_INFO);
         assertTrue(mQueue.isIdleLocked());
         assertTrue(mQueue.isBeyondBarrierLocked(beforeFirst));
         assertTrue(mQueue.isBeyondBarrierLocked(afterFirst));
@@ -2046,9 +2056,9 @@
         final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
         final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW);
 
-        receiverGreenApp.setCached(true);
-        receiverBlueApp.setCached(true);
-        receiverYellowApp.setCached(false);
+        freezeUid(getUidForPackage(PACKAGE_GREEN));
+        freezeUid(getUidForPackage(PACKAGE_BLUE));
+        thawUid(getUidForPackage(PACKAGE_YELLOW));
 
         final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
         final BroadcastOptions opts = BroadcastOptions.makeBasic()
@@ -2071,8 +2081,7 @@
         verifyScheduleRegisteredReceiver(times(1), receiverYellowApp, airplane);
 
         // Shift green to be active and confirm that deferred broadcast is delivered
-        receiverGreenApp.setCached(false);
-        mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), false);
+        thawUid(getUidForPackage(PACKAGE_GREEN));
         waitForIdle();
         verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, airplane);
     }
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/HardwareKeyboardShortcutControllerTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/HardwareKeyboardShortcutControllerTest.java
new file mode 100644
index 0000000..6eedeea
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/HardwareKeyboardShortcutControllerTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.inputmethod;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class HardwareKeyboardShortcutControllerTest {
+
+    @Test
+    public void testForwardRotation() {
+        final List<String> handles = Arrays.asList("0", "1", "2", "3");
+        assertEquals("2", HardwareKeyboardShortcutController.getNeighborItem(handles, "1", true));
+        assertEquals("3", HardwareKeyboardShortcutController.getNeighborItem(handles, "2", true));
+        assertEquals("0", HardwareKeyboardShortcutController.getNeighborItem(handles, "3", true));
+    }
+
+    @Test
+    public void testBackwardRotation() {
+        final List<String> handles = Arrays.asList("0", "1", "2", "3");
+        assertEquals("0", HardwareKeyboardShortcutController.getNeighborItem(handles, "1", false));
+        assertEquals("3", HardwareKeyboardShortcutController.getNeighborItem(handles, "0", false));
+        assertEquals("2", HardwareKeyboardShortcutController.getNeighborItem(handles, "3", false));
+    }
+
+    @Test
+    public void testNotMatching() {
+        final List<String> handles = Arrays.asList("0", "1", "2", "3");
+        assertNull(HardwareKeyboardShortcutController.getNeighborItem(handles, "X", true));
+        assertNull(HardwareKeyboardShortcutController.getNeighborItem(handles, "X", false));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java b/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
index 397d7b5..7cf5bc8 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
@@ -26,8 +26,9 @@
 
 import android.content.Context;
 import android.os.Handler;
+import android.util.IntArray;
 import android.util.SparseArray;
-import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -57,10 +58,24 @@
     private static final int TEST_UID_4 = 56926423;
     private static final int TEST_UID_5 = 76421423;
 
+    private static final int TEST_PROC_STATE_1 = 72331;
+    private static final int TEST_PROC_STATE_2 = 792351;
+    private static final int TEST_PROC_STATE_3 = 138831;
+    private static final int TEST_PROC_STATE_4 = 23231;
+    private static final int TEST_PROC_STATE_5 = 42;
+
     private static final Context sContext = InstrumentationRegistry.getTargetContext();
     private final Handler mHandler = Mockito.mock(Handler.class);
     private final ThreadLocalRandom mRandom = ThreadLocalRandom.current();
 
+    private void populateDefaultProcStates(CpuWakeupStats obj) {
+        obj.mUidProcStates.put(TEST_UID_1, TEST_PROC_STATE_1);
+        obj.mUidProcStates.put(TEST_UID_2, TEST_PROC_STATE_2);
+        obj.mUidProcStates.put(TEST_UID_3, TEST_PROC_STATE_3);
+        obj.mUidProcStates.put(TEST_UID_4, TEST_PROC_STATE_4);
+        obj.mUidProcStates.put(TEST_UID_5, TEST_PROC_STATE_5);
+    }
+
     @Test
     public void removesOldWakeups() {
         // The xml resource doesn't matter for this test.
@@ -96,6 +111,8 @@
         final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
         final long wakeupTime = 12423121;
 
+        populateDefaultProcStates(obj);
+
         obj.noteWakeupTimeAndReason(wakeupTime, 1, KERNEL_REASON_ALARM_IRQ);
 
         // Outside the window, so should be ignored.
@@ -106,15 +123,20 @@
         // Should be attributed
         obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3, TEST_UID_5);
 
-        final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+        final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
         assertThat(attribution).isNotNull();
         assertThat(attribution.size()).isEqualTo(1);
         assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_ALARM)).isTrue();
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_1)).isEqualTo(false);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_2)).isEqualTo(false);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(true);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_4)).isEqualTo(false);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_5)).isEqualTo(true);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_1)).isLessThan(
+                0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_2)).isLessThan(
+                0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(
+                TEST_PROC_STATE_3);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_4)).isLessThan(
+                0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_5)).isEqualTo(
+                TEST_PROC_STATE_5);
     }
 
     @Test
@@ -122,6 +144,8 @@
         final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
         final long wakeupTime = 12423121;
 
+        populateDefaultProcStates(obj);
+
         obj.noteWakeupTimeAndReason(wakeupTime, 1, KERNEL_REASON_WIFI_IRQ);
 
         // Outside the window, so should be ignored.
@@ -132,15 +156,17 @@
         // Should be attributed
         obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime + 3, TEST_UID_4, TEST_UID_5);
 
-        final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+        final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
         assertThat(attribution).isNotNull();
         assertThat(attribution.size()).isEqualTo(1);
         assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_WIFI)).isTrue();
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_1)).isEqualTo(false);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_2)).isEqualTo(false);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_3)).isEqualTo(false);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_4)).isEqualTo(true);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_5)).isEqualTo(true);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_1)).isLessThan(0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_2)).isLessThan(0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_3)).isLessThan(0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_4)).isEqualTo(
+                TEST_PROC_STATE_4);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_5)).isEqualTo(
+                TEST_PROC_STATE_5);
     }
 
     @Test
@@ -148,6 +174,8 @@
         final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
         final long wakeupTime = 92123210;
 
+        populateDefaultProcStates(obj);
+
         obj.noteWakeupTimeAndReason(wakeupTime, 4,
                 KERNEL_REASON_WIFI_IRQ + ":" + KERNEL_REASON_ALARM_IRQ);
 
@@ -173,23 +201,31 @@
         obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime - 1, TEST_UID_2,
                 TEST_UID_5);
 
-        final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+        final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
         assertThat(attribution).isNotNull();
         assertThat(attribution.size()).isEqualTo(2);
 
         assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_ALARM)).isTrue();
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_1)).isEqualTo(false);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_2)).isEqualTo(false);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(true);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_4)).isEqualTo(true);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_5)).isEqualTo(true);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_1)).isLessThan(
+                0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_2)).isLessThan(
+                0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(
+                TEST_PROC_STATE_3);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_4)).isEqualTo(
+                TEST_PROC_STATE_4);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_5)).isEqualTo(
+                TEST_PROC_STATE_5);
 
         assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_WIFI)).isTrue();
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_1)).isEqualTo(true);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_2)).isEqualTo(true);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_3)).isEqualTo(false);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_4)).isEqualTo(false);
-        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_5)).isEqualTo(true);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_1)).isEqualTo(
+                TEST_PROC_STATE_1);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_2)).isEqualTo(
+                TEST_PROC_STATE_2);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_3)).isLessThan(0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_4)).isLessThan(0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_5)).isEqualTo(
+                TEST_PROC_STATE_5);
     }
 
     @Test
@@ -206,12 +242,12 @@
         obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime - 3, TEST_UID_4,
                 TEST_UID_5);
 
-        final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+        final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
         assertThat(attribution).isNotNull();
         assertThat(attribution.size()).isEqualTo(1);
         assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_UNKNOWN)).isTrue();
-        final SparseBooleanArray uids = attribution.get(CPU_WAKEUP_SUBSYSTEM_UNKNOWN);
-        assertThat(uids == null || uids.size() == 0).isTrue();
+        final SparseIntArray uidProcStates = attribution.get(CPU_WAKEUP_SUBSYSTEM_UNKNOWN);
+        assertThat(uidProcStates == null || uidProcStates.size() == 0).isTrue();
     }
 
     @Test
@@ -259,4 +295,39 @@
         // Any nearby activity should not end up in the attribution map.
         assertThat(obj.mWakeupAttribution.size()).isEqualTo(0);
     }
+
+    @Test
+    public void uidProcStateBookkeeping() {
+        final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
+
+        assertThat(obj.mUidProcStates.size()).isEqualTo(0);
+
+        final IntArray uids = new IntArray(87);
+        for (int i = 0; i < 87; i++) {
+            final int uid = mRandom.nextInt(1 << 20);
+            if (uids.indexOf(uid) < 0) {
+                uids.add(uid);
+            }
+        }
+
+        for (int i = 0; i < uids.size(); i++) {
+            final int uid = uids.get(i);
+            for (int j = 0; j < 43; j++) {
+                final int procState = mRandom.nextInt(1 << 15);
+                obj.noteUidProcessState(uid, procState);
+                assertThat(obj.mUidProcStates.get(uid)).isEqualTo(procState);
+            }
+            assertThat(obj.mUidProcStates.size()).isEqualTo(i + 1);
+        }
+
+        for (int i = 0; i < uids.size(); i++) {
+            obj.onUidRemoved(uids.get(i));
+            assertThat(obj.mUidProcStates.indexOfKey(uids.get(i))).isLessThan(0);
+        }
+
+        assertThat(obj.mUidProcStates.size()).isEqualTo(0);
+
+        obj.onUidRemoved(213);
+        assertThat(obj.mUidProcStates.size()).isEqualTo(0);
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 42d1ace..37dd9f5 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -1638,12 +1638,6 @@
                 any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);
         mContext.getTestablePermissions().setPermission(
                 android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
-        DeviceConfig.setProperty(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED,
-                "true",
-                false);
-        Thread.sleep(300);
 
         final String tag = "testEnqueueNotificationWithTag_FgsAddsFlags_dismissalAllowed";
 
@@ -1665,38 +1659,6 @@
     }
 
     @Test
-    public void testEnqueueNotificationWithTag_FGSaddsFlags_dismissalNotAllowed() throws Exception {
-        when(mAmi.applyForegroundServiceNotification(
-                any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);
-        mContext.getTestablePermissions().setPermission(
-                android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
-        DeviceConfig.setProperty(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED,
-                "false",
-                false);
-        Thread.sleep(300);
-
-        final String tag = "testEnqueueNotificationWithTag_FGSaddsNoClear";
-
-        Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
-                .setContentTitle("foo")
-                .setSmallIcon(android.R.drawable.sym_def_app_icon)
-                .setFlag(FLAG_FOREGROUND_SERVICE, true)
-                .build();
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
-                n, UserHandle.getUserHandleForUid(mUid), null, 0);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, tag,
-                sbn.getId(), sbn.getNotification(), sbn.getUserId());
-        waitForIdle();
-
-        StatusBarNotification[] notifs =
-                mBinderService.getActiveNotifications(PKG);
-        assertThat(notifs[0].getNotification().flags).isEqualTo(
-                FLAG_FOREGROUND_SERVICE | FLAG_CAN_COLORIZE | FLAG_NO_CLEAR | FLAG_ONGOING_EVENT);
-    }
-
-    @Test
     public void testEnqueueNotificationWithTag_nullAction_fixed() throws Exception {
         Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
                 .setContentTitle("foo")
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java
new file mode 100644
index 0000000..ff2ce15
--- /dev/null
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.soundtrigger;
+
+import static android.hardware.soundtrigger.ConversionUtil.aidl2apiAudioFormatWithDefault;
+import static android.hardware.soundtrigger.ConversionUtil.aidl2apiPhrase;
+import static android.hardware.soundtrigger.ConversionUtil.aidl2apiRecognitionConfig;
+import static android.hardware.soundtrigger.ConversionUtil.api2aidlPhrase;
+import static android.hardware.soundtrigger.ConversionUtil.api2aidlRecognitionConfig;
+import static android.hardware.soundtrigger.ConversionUtil.byteArrayToSharedMemory;
+import static android.hardware.soundtrigger.ConversionUtil.sharedMemoryToByteArray;
+import static android.hardware.soundtrigger.SoundTrigger.ConfidenceLevel;
+import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_GENERIC;
+import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_USER_AUTHENTICATION;
+import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION;
+import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.hardware.soundtrigger.ConversionUtil;
+import android.hardware.soundtrigger.SoundTrigger;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+@RunWith(AndroidJUnit4.class)
+public class ConversionUtilTest {
+    private static final String TAG = "ConversionUtilTest";
+
+    @Test
+    public void testDefaultAudioFormatConstruction() {
+        // This method should generate a real format when passed null
+        final var format = aidl2apiAudioFormatWithDefault(
+                null /** exercise default **/,
+                true /** isInput **/
+                );
+        assertNotNull(format);
+    }
+
+    @Test
+    public void testRecognitionConfigRoundTrip() {
+        final int flags = SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION
+                | SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION;
+        final var data = new byte[] {0x11, 0x22};
+        final var keyphrases = new SoundTrigger.KeyphraseRecognitionExtra[2];
+        keyphrases[0] = new SoundTrigger.KeyphraseRecognitionExtra(99,
+                RECOGNITION_MODE_VOICE_TRIGGER | RECOGNITION_MODE_USER_IDENTIFICATION, 13,
+                    new ConfidenceLevel[] {new ConfidenceLevel(9999, 50),
+                                           new ConfidenceLevel(5000, 80)});
+        keyphrases[1] = new SoundTrigger.KeyphraseRecognitionExtra(101,
+                RECOGNITION_MODE_GENERIC, 8, new ConfidenceLevel[] {
+                    new ConfidenceLevel(7777, 30),
+                    new ConfidenceLevel(2222, 60)});
+
+        var apiconfig = new SoundTrigger.RecognitionConfig(true, false /** must be false **/,
+                keyphrases, data, flags);
+        assertEquals(apiconfig, aidl2apiRecognitionConfig(api2aidlRecognitionConfig(apiconfig)));
+    }
+
+    @Test
+    public void testByteArraySharedMemRoundTrip() {
+        final var data = new byte[] { 0x11, 0x22, 0x33, 0x44,
+                (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef };
+        assertArrayEquals(data, sharedMemoryToByteArray(byteArrayToSharedMemory(data, "name"),
+                    10000000));
+
+    }
+
+    @Test
+    public void testPhraseRoundTrip() {
+        final var users = new int[] {10001, 10002};
+        final var apiphrase = new SoundTrigger.Keyphrase(17 /** id **/,
+                RECOGNITION_MODE_VOICE_TRIGGER | RECOGNITION_MODE_USER_AUTHENTICATION,
+                Locale.forLanguageTag("no_NO"),
+                "Hello Android", /** keyphrase **/
+                users);
+        assertEquals(apiphrase, aidl2apiPhrase(api2aidlPhrase(apiphrase)));
+    }
+}
diff --git a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/SoundTriggerTest.java
similarity index 89%
rename from tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java
rename to services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/SoundTriggerTest.java
index f49d9c9..e6a1be8 100644
--- a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/SoundTriggerTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.hardware.soundtrigger;
+package com.android.server.soundtrigger;
 
 import android.hardware.soundtrigger.SoundTrigger.ConfidenceLevel;
 import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
@@ -22,6 +22,7 @@
 import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra;
 import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
 import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
+import android.hardware.soundtrigger.SoundTrigger;
 import android.media.AudioFormat;
 import android.os.Parcel;
 import android.test.InstrumentationTestCase;
@@ -50,10 +51,7 @@
         Keyphrase unparceled = Keyphrase.CREATOR.createFromParcel(parcel);
 
         // Verify that they are the same
-        assertEquals(keyphrase.getId(), unparceled.getId());
-        assertNull(unparceled.getUsers());
-        assertEquals(keyphrase.getLocale(), unparceled.getLocale());
-        assertEquals(keyphrase.getText(), unparceled.getText());
+        assertEquals(keyphrase, unparceled);
     }
 
     @SmallTest
@@ -115,10 +113,7 @@
         KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
 
         // Verify that they are the same
-        assertEquals(ksm.getUuid(), unparceled.getUuid());
-        assertNull(unparceled.getData());
-        assertEquals(ksm.getType(), unparceled.getType());
-        assertTrue(Arrays.equals(keyphrases, unparceled.getKeyphrases()));
+        assertEquals(ksm, unparceled);
     }
 
     @SmallTest
@@ -162,10 +157,7 @@
         KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
 
         // Verify that they are the same
-        assertEquals(ksm.getUuid(), unparceled.getUuid());
-        assertEquals(ksm.getType(), unparceled.getType());
-        assertNull(unparceled.getKeyphrases());
-        assertTrue(Arrays.equals(ksm.getData(), unparceled.getData()));
+        assertEquals(ksm, unparceled);
     }
 
     @SmallTest
@@ -226,7 +218,11 @@
                 3 /* captureDelayMs */,
                 4 /* capturePreambleMs */,
                 false /* triggerInData */,
-                null /* captureFormat */,
+                new AudioFormat.Builder()
+                        .setSampleRate(16000)
+                        .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+                        .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+                        .build(),
                 null /* data */,
                 12345678 /* halEventReceivedMillis */);
 
@@ -251,7 +247,11 @@
                 3 /* captureDelayMs */,
                 4 /* capturePreambleMs */,
                 false /* triggerInData */,
-                null /* captureFormat */,
+                new AudioFormat.Builder()
+                        .setSampleRate(16000)
+                        .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+                        .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+                        .build(),
                 new byte[1] /* data */,
                 12345678 /* halEventReceivedMillis */);
 
@@ -278,7 +278,11 @@
                 3 /* captureDelayMs */,
                 4 /* capturePreambleMs */,
                 false /* triggerInData */,
-                null /* captureFormat */,
+                new AudioFormat.Builder()
+                        .setSampleRate(16000)
+                        .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+                        .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+                        .build(),
                 data,
                 12345678 /* halEventReceivedMillis */);
 
@@ -335,7 +339,11 @@
                 3 /* captureDelayMs */,
                 4 /* capturePreambleMs */,
                 false /* triggerInData */,
-                null /* captureFormat */,
+                new AudioFormat.Builder()
+                        .setSampleRate(16000)
+                        .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+                        .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+                        .build(),
                 null /* data */,
                 null /* keyphraseExtras */,
                 12345678 /* halEventReceivedMillis */);
@@ -364,7 +372,11 @@
                 3 /* captureDelayMs */,
                 4 /* capturePreambleMs */,
                 false /* triggerInData */,
-                null /* captureFormat */,
+                new AudioFormat.Builder()
+                        .setSampleRate(16000)
+                        .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+                        .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+                        .build(),
                 new byte[1] /* data */,
                 kpExtra,
                 12345678 /* halEventReceivedMillis */);
@@ -409,7 +421,11 @@
                 3 /* captureDelayMs */,
                 4 /* capturePreambleMs */,
                 false /* triggerInData */,
-                null /* captureFormat */,
+                new AudioFormat.Builder()
+                        .setSampleRate(16000)
+                        .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+                        .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+                        .build(),
                 data,
                 kpExtra,
                 12345678 /* halEventReceivedMillis */);
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java
index 5661b12..7b7a0a3 100644
--- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java
@@ -16,36 +16,18 @@
 
 package com.android.server.soundtrigger_middleware;
 
-import static android.hardware.soundtrigger.ConversionUtil.aidl2apiAudioFormatWithDefault;
-import static android.hardware.soundtrigger.ConversionUtil.aidl2apiPhrase;
-import static android.hardware.soundtrigger.ConversionUtil.aidl2apiRecognitionConfig;
-import static android.hardware.soundtrigger.ConversionUtil.api2aidlPhrase;
-import static android.hardware.soundtrigger.ConversionUtil.api2aidlRecognitionConfig;
-import static android.hardware.soundtrigger.ConversionUtil.byteArrayToSharedMemory;
-import static android.hardware.soundtrigger.ConversionUtil.sharedMemoryToByteArray;
-import static android.hardware.soundtrigger.SoundTrigger.ConfidenceLevel;
-import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_GENERIC;
-import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_USER_AUTHENTICATION;
-import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION;
-import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER;
-
-import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
 
 import android.hardware.audio.common.V2_0.Uuid;
-import android.hardware.soundtrigger.SoundTrigger;
 
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.Locale;
-
 @RunWith(AndroidJUnit4.class)
 public class ConversionUtilTest {
-    private static final String TAG = "ConversionUtilTest";
+    private static final String TAG = "SoundTriggerMiddlewareConversionUtilTest";
 
     @Test
     public void testUuidRoundTrip() {
@@ -62,54 +44,4 @@
         Uuid reconstructed = ConversionUtil.aidl2hidlUuid(aidl);
         assertEquals(hidl, reconstructed);
     }
-
-    @Test
-    public void testDefaultAudioFormatConstruction() {
-        // This method should generate a real format when passed null
-        final var format = aidl2apiAudioFormatWithDefault(
-                null /** exercise default **/,
-                true /** isInput **/
-                );
-        assertNotNull(format);
-    }
-
-    @Test
-    public void testRecognitionConfigRoundTrip() {
-        final int flags = SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION
-                | SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION;
-        final var data = new byte[] {0x11, 0x22};
-        final var keyphrases = new SoundTrigger.KeyphraseRecognitionExtra[2];
-        keyphrases[0] = new SoundTrigger.KeyphraseRecognitionExtra(99,
-                RECOGNITION_MODE_VOICE_TRIGGER | RECOGNITION_MODE_USER_IDENTIFICATION, 13,
-                    new ConfidenceLevel[] {new ConfidenceLevel(9999, 50),
-                                           new ConfidenceLevel(5000, 80)});
-        keyphrases[1] = new SoundTrigger.KeyphraseRecognitionExtra(101,
-                RECOGNITION_MODE_GENERIC, 8, new ConfidenceLevel[] {
-                    new ConfidenceLevel(7777, 30),
-                    new ConfidenceLevel(2222, 60)});
-
-        var apiconfig = new SoundTrigger.RecognitionConfig(true, false /** must be false **/,
-                keyphrases, data, flags);
-        assertEquals(apiconfig, aidl2apiRecognitionConfig(api2aidlRecognitionConfig(apiconfig)));
-    }
-
-    @Test
-    public void testByteArraySharedMemRoundTrip() {
-        final var data = new byte[] { 0x11, 0x22, 0x33, 0x44,
-                (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef };
-        assertArrayEquals(data, sharedMemoryToByteArray(byteArrayToSharedMemory(data, "name"),
-                    10000000));
-
-    }
-
-    @Test
-    public void testPhraseRoundTrip() {
-        final var users = new int[] {10001, 10002};
-        final var apiphrase = new SoundTrigger.Keyphrase(17 /** id **/,
-                RECOGNITION_MODE_VOICE_TRIGGER | RECOGNITION_MODE_USER_AUTHENTICATION,
-                Locale.forLanguageTag("no_NO"),
-                "Hello Android", /** keyphrase **/
-                users);
-        assertEquals(apiphrase, aidl2apiPhrase(api2aidlPhrase(apiphrase)));
-    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 20d410c..2914de1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -81,6 +81,17 @@
         return win;
     }
 
+    private WindowState createDreamWindow() {
+        final WindowState win = createDreamWindow(null, TYPE_BASE_APPLICATION, "dream");
+        final WindowManager.LayoutParams attrs = win.mAttrs;
+        attrs.width = MATCH_PARENT;
+        attrs.height = MATCH_PARENT;
+        attrs.flags =
+                FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+        attrs.format = PixelFormat.OPAQUE;
+        return win;
+    }
+
     private WindowState createDimmingDialogWindow(boolean canBeImTarget) {
         final WindowState win = spy(createWindow(null, TYPE_APPLICATION, "dimmingDialog"));
         final WindowManager.LayoutParams attrs = win.mAttrs;
@@ -384,4 +395,25 @@
         displayPolicy.requestTransientBars(mNavBarWindow, true);
         assertTrue(mDisplayContent.getInsetsPolicy().isTransient(navigationBars()));
     }
+
+    @UseTestDisplay(addWindows = { W_NAVIGATION_BAR })
+    @Test
+    public void testTransientBarsSuppressedOnDreams() {
+        final WindowState win = createDreamWindow();
+
+        ((TestWindowManagerPolicy) mWm.mPolicy).mIsUserSetupComplete = true;
+        win.mAttrs.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+        win.setRequestedVisibleTypes(0, navigationBars());
+
+        final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
+        displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
+        final InsetsSourceProvider navBarProvider = mNavBarWindow.getControllableInsetProvider();
+        navBarProvider.updateControlForTarget(win, false);
+        navBarProvider.getSource().setVisible(false);
+
+        displayPolicy.setCanSystemBarsBeShownByUser(true);
+        displayPolicy.requestTransientBars(mNavBarWindow, true);
+
+        assertFalse(mDisplayContent.getInsetsPolicy().isTransient(navigationBars()));
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index d7bf4b0..90506d4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1885,6 +1885,39 @@
         assertEquals(newParent.getDisplayArea(), change.mCommonAncestor);
     }
 
+    @Test
+    public void testMoveToTopWhileVisible() {
+        final Transition transition = createTestTransition(TRANSIT_OPEN);
+        final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
+        final ArraySet<WindowContainer> participants = transition.mParticipants;
+
+        // Start with taskB on top and taskA on bottom but both visible.
+        final Task rootTaskA = createTask(mDisplayContent);
+        final Task leafTaskA = createTaskInRootTask(rootTaskA, 0 /* userId */);
+        final Task taskB = createTask(mDisplayContent);
+        leafTaskA.setVisibleRequested(true);
+        taskB.setVisibleRequested(true);
+        // manually collect since this is a test transition and not known by transitionController.
+        transition.collect(leafTaskA);
+        rootTaskA.moveToFront("test", leafTaskA);
+
+        // All the tasks were already visible, so there shouldn't be any changes
+        ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
+                participants, changes);
+        assertTrue(targets.isEmpty());
+
+        // After collecting order changes, it should recognize that a task moved to top.
+        transition.collectOrderChanges();
+        targets = Transition.calculateTargets(participants, changes);
+        assertEquals(1, targets.size());
+
+        // Make sure the flag is set
+        final TransitionInfo info = Transition.calculateTransitionInfo(
+                transition.mType, 0 /* flags */, targets, mMockT);
+        assertTrue((info.getChanges().get(0).getFlags() & TransitionInfo.FLAG_MOVED_TO_TOP) != 0);
+        assertEquals(TRANSIT_CHANGE, info.getChanges().get(0).getMode());
+    }
+
     private static void makeTaskOrganized(Task... tasks) {
         final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
         for (Task t : tasks) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 0d7cdc8..7e3ec55 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import static android.app.AppOpsManager.OP_NONE;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -467,6 +468,12 @@
         return createWindow(null, type, activity, name);
     }
 
+    WindowState createDreamWindow(WindowState parent, int type, String name) {
+        final WindowToken token = createWindowToken(
+                mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_DREAM, type);
+        return createWindow(parent, type, token, name);
+    }
+
     // TODO: Move these calls to a builder?
     WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name,
             IWindow iwindow) {
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/FakeSoundTriggerHal.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/FakeSoundTriggerHal.java
index 86c4bbf..37a325e 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/FakeSoundTriggerHal.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/FakeSoundTriggerHal.java
@@ -276,16 +276,15 @@
         // for our clients.
         mGlobalEventSession = new IInjectGlobalEvent.Stub() {
             /**
-             * Overrides IInjectGlobalEvent method.
              * Simulate a HAL process restart. This method is not included in regular HAL interface,
              * since the entire process is restarted by sending a signal.
              * Since we run in-proc, we must offer an explicit restart method.
              * oneway
              */
             @Override
-            public void triggerRestart() throws RemoteException {
+            public void triggerRestart() {
                 synchronized (FakeSoundTriggerHal.this.mLock) {
-                    if (mIsDead) throw new DeadObjectException();
+                    if (mIsDead) return;
                     mIsDead = true;
                     mInjectionDispatcher.wrap((ISoundTriggerInjection cb) ->
                             cb.onRestarted(this));
@@ -305,15 +304,15 @@
                 }
             }
 
-            /**
-             * Overrides IInjectGlobalEvent method.
-             * oneway
-             */
+            // oneway
             @Override
             public void setResourceContention(boolean isResourcesContended,
-                        IAcknowledgeEvent callback) throws RemoteException {
+                        IAcknowledgeEvent callback) {
                 synchronized (FakeSoundTriggerHal.this.mLock) {
-                    if (mIsDead) throw new DeadObjectException();
+                    // oneway, so don't throw on death
+                    if (mIsDead || mIsResourceContended == isResourcesContended) {
+                        return;
+                    }
                     mIsResourceContended = isResourcesContended;
                     // Introducing contention is the only injection which can't be
                     // observed by the ST client.
@@ -325,7 +324,19 @@
                     }
                 }
             }
+
+            // oneway
+            @Override
+            public void triggerOnResourcesAvailable() {
+                synchronized (FakeSoundTriggerHal.this.mLock) {
+                    // oneway, so don't throw on death
+                    if (mIsDead) return;
+                    mGlobalCallbackDispatcher.wrap((ISoundTriggerHwGlobalCallback cb) ->
+                            cb.onResourcesAvailable());
+                }
+            }
         };
+
         // Register the global event injection interface
         mInjectionDispatcher.wrap((ISoundTriggerInjection cb)
                 -> cb.registerGlobalEventInjection(mGlobalEventSession));
@@ -465,7 +476,9 @@
             if (session == null) {
                 Slog.wtf(TAG, "Attempted to start recognition with invalid handle");
             }
-
+            if (mIsResourceContended) {
+                throw new ServiceSpecificException(Status.RESOURCE_CONTENTION);
+            }
             if (session.getIsUnloaded()) {
                 // TODO(b/274470274) this is a deficiency in the existing HAL API, there is no way
                 // to handle this race gracefully
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
index 2f8d17d..4c134af 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
@@ -35,7 +35,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.util.Log;
+import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
@@ -463,7 +463,7 @@
                 printObject(originatorIdentity),
                 printArgs(args),
                 printObject(retVal));
-        Log.i(TAG, message);
+        Slog.i(TAG, message);
         appendMessage(message);
     }
 
@@ -474,7 +474,7 @@
                 object,
                 printObject(originatorIdentity),
                 printArgs(args));
-        Log.i(TAG, message);
+        Slog.i(TAG, message);
         appendMessage(message);
     }
 
@@ -486,7 +486,7 @@
                 object,
                 printObject(originatorIdentity),
                 printArgs(args));
-        Log.e(TAG, message, ex);
+        Slog.e(TAG, message, ex);
         appendMessage(message + " " + ex.toString());
     }
 
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index fa4c9b2..ff378ba 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -43,6 +43,7 @@
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
 import java.util.function.Supplier;
 
 /**
@@ -325,11 +326,19 @@
     }
 
     private void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
+        // This is purposefully not synchronized with broadcastToCallbacksLocked because the
+        // list of callbacks to notify is copied over from the original list modified here. I also
+        // do not want to risk introducing a deadlock by using the same mCallbacks Object to
+        // synchronize on outgoing and incoming operations.
         mCallbacks.register(c);
         updateNewCallbackWithState(c);
     }
 
     private void removeRegistrationCallback(IImsRegistrationCallback c) {
+        // This is purposefully not synchronized with broadcastToCallbacksLocked because the
+        // list of callbacks to notify is copied over from the original list modified here. I also
+        // do not want to risk introducing a deadlock by using the same mCallbacks Object to
+        // synchronize on outgoing and incoming operations.
         mCallbacks.unregister(c);
     }
 
@@ -420,7 +429,7 @@
     @SystemApi
     public final void onRegistered(@NonNull ImsRegistrationAttributes attributes) {
         updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERED);
-        mCallbacks.broadcastAction((c) -> {
+        broadcastToCallbacksLocked((c) -> {
             try {
                 c.onRegistered(attributes);
             } catch (RemoteException e) {
@@ -449,7 +458,7 @@
     @SystemApi
     public final void onRegistering(@NonNull ImsRegistrationAttributes attributes) {
         updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERING);
-        mCallbacks.broadcastAction((c) -> {
+        broadcastToCallbacksLocked((c) -> {
             try {
                 c.onRegistering(attributes);
             } catch (RemoteException e) {
@@ -507,7 +516,7 @@
         updateToDisconnectedState(info, suggestedAction, imsRadioTech);
         // ImsReasonInfo should never be null.
         final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
-        mCallbacks.broadcastAction((c) -> {
+        broadcastToCallbacksLocked((c) -> {
             try {
                 c.onDeregistered(reasonInfo, suggestedAction, imsRadioTech);
             } catch (RemoteException e) {
@@ -569,7 +578,7 @@
         updateToDisconnectedState(info, suggestedAction, imsRadioTech);
         // ImsReasonInfo should never be null.
         final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
-        mCallbacks.broadcastAction((c) -> {
+        broadcastToCallbacksLocked((c) -> {
             try {
                 c.onDeregisteredWithDetails(reasonInfo, suggestedAction, imsRadioTech, details);
             } catch (RemoteException e) {
@@ -591,7 +600,7 @@
     public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
             ImsReasonInfo info) {
         final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
-        mCallbacks.broadcastAction((c) -> {
+        broadcastToCallbacksLocked((c) -> {
             try {
                 c.onTechnologyChangeFailed(imsRadioTech, reasonInfo);
             } catch (RemoteException e) {
@@ -614,7 +623,20 @@
             mUris = ArrayUtils.cloneOrNull(uris);
             mUrisSet = true;
         }
-        mCallbacks.broadcastAction((c) -> onSubscriberAssociatedUriChanged(c, uris));
+        broadcastToCallbacksLocked((c) -> onSubscriberAssociatedUriChanged(c, uris));
+    }
+
+    /**
+     * Broadcast the specified operation in a synchronized manner so that multiple threads do not
+     * try to call broadcast at the same time, which will generate an error.
+     * @param c The Consumer lambda method containing the callback to call.
+     */
+    private void broadcastToCallbacksLocked(Consumer<IImsRegistrationCallback> c) {
+        // One broadcast can happen at a time, so synchronize threads so only one
+        // beginBroadcast/endBroadcast happens at a time.
+        synchronized (mCallbacks) {
+            mCallbacks.broadcastAction(c);
+        }
     }
 
     private void onSubscriberAssociatedUriChanged(IImsRegistrationCallback callback, Uri[] uris) {
diff --git a/telephony/java/android/telephony/satellite/ISatelliteDatagramCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteDatagramCallback.aidl
index 2954c2d..e229f05 100644
--- a/telephony/java/android/telephony/satellite/ISatelliteDatagramCallback.aidl
+++ b/telephony/java/android/telephony/satellite/ISatelliteDatagramCallback.aidl
@@ -18,7 +18,7 @@
 
 import android.telephony.satellite.SatelliteDatagram;
 
-import com.android.internal.telephony.ILongConsumer;
+import com.android.internal.telephony.IVoidConsumer;
 
 /**
  * Interface for satellite datagrams callback.
@@ -31,10 +31,10 @@
      * @param datagramId An id that uniquely identifies incoming datagram.
      * @param datagram Datagram received from satellite.
      * @param pendingCount Number of datagrams yet to be received from satellite.
-     * @param callback This callback will be used by datagram receiver app to send received
-     *                 datagramId to Telephony. If the callback is not received within five minutes,
-     *                 Telephony will resend the datagram.
+     * @param callback This callback will be used by datagram receiver app to to inform
+     *                 Telephony that datagram is received. If the callback is not received
+     *                 within five minutes, Telephony will resend the datagram.
      */
     void onSatelliteDatagramReceived(long datagramId, in SatelliteDatagram datagram,
-            int pendingCount, ILongConsumer callback);
+            int pendingCount, IVoidConsumer callback);
 }
diff --git a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
index d8a6faf..d0409bf 100644
--- a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
@@ -19,7 +19,7 @@
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 
-import com.android.internal.telephony.ILongConsumer;
+import java.util.function.Consumer;
 
 /**
  * A callback class for listening to satellite datagrams.
@@ -33,11 +33,11 @@
      * @param datagramId An id that uniquely identifies incoming datagram.
      * @param datagram Datagram to be received over satellite.
      * @param pendingCount Number of datagrams yet to be received by the app.
-     * @param callback This callback will be used by datagram receiver app to send received
-     *                 datagramId to Telephony. If the callback is not received within five minutes,
-     *                 Telephony will resend the datagram.
+     * @param callback This callback will be used by datagram receiver app to inform Telephony
+     *                 that they received the datagram. If the callback is not received within
+     *                 five minutes, Telephony will resend the datagram.
      */
     @UnsupportedAppUsage
     void onSatelliteDatagramReceived(long datagramId, @NonNull SatelliteDatagram datagram,
-            int pendingCount, @NonNull ILongConsumer callback);
+            int pendingCount, @NonNull Consumer<Void> callback);
 }
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 7d82fd8..20f9bc8b 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -37,7 +37,7 @@
 import android.telephony.TelephonyFrameworkInitializer;
 
 import com.android.internal.telephony.IIntegerConsumer;
-import com.android.internal.telephony.ILongConsumer;
+import com.android.internal.telephony.IVoidConsumer;
 import com.android.internal.telephony.ITelephony;
 import com.android.telephony.Rlog;
 
@@ -862,7 +862,7 @@
      *
      * @param token The token to be used as a unique identifier for provisioning with satellite
      *              gateway.
-     * @param regionId The region ID for the device's current location.
+     * @param provisionData Data from the provisioning app that can be used by provisioning server
      * @param cancellationSignal The optional signal used by the caller to cancel the provision
      *                           request. Even when the cancellation is signaled, Telephony will
      *                           still trigger the callback to return the result of this request.
@@ -874,13 +874,14 @@
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
     @UnsupportedAppUsage
-    public void provisionSatelliteService(@NonNull String token, @NonNull String regionId,
+    public void provisionSatelliteService(@NonNull String token, @NonNull byte[] provisionData,
             @Nullable CancellationSignal cancellationSignal,
             @NonNull @CallbackExecutor Executor executor,
             @SatelliteError @NonNull Consumer<Integer> resultListener) {
         Objects.requireNonNull(token);
         Objects.requireNonNull(executor);
         Objects.requireNonNull(resultListener);
+        Objects.requireNonNull(provisionData);
 
         ICancellationSignal cancelRemote = null;
         try {
@@ -893,7 +894,7 @@
                                 () -> resultListener.accept(result)));
                     }
                 };
-                cancelRemote = telephony.provisionSatelliteService(mSubId, token, regionId,
+                cancelRemote = telephony.provisionSatelliteService(mSubId, token, provisionData,
                         errorCallback);
             } else {
                 throw new IllegalStateException("telephony service is null.");
@@ -1186,10 +1187,22 @@
                             @Override
                             public void onSatelliteDatagramReceived(long datagramId,
                                     @NonNull SatelliteDatagram datagram, int pendingCount,
-                                    @NonNull ILongConsumer ack) {
+                                    @NonNull IVoidConsumer internalAck) {
+                                Consumer<Void> externalAck = new Consumer<Void>() {
+                                    @Override
+                                    public void accept(Void result) {
+                                        try {
+                                            internalAck.accept();
+                                        }  catch (RemoteException e) {
+                                              logd("onSatelliteDatagramReceived "
+                                                      + "RemoteException: " + e);
+                                        }
+                                    }
+                                };
+
                                 executor.execute(() -> Binder.withCleanCallingIdentity(
                                         () -> callback.onSatelliteDatagramReceived(
-                                                datagramId, datagram, pendingCount, ack)));
+                                                datagramId, datagram, pendingCount, externalAck)));
                             }
                         };
                 sSatelliteDatagramCallbackMap.put(callback, internalCallback);
@@ -1244,7 +1257,7 @@
      * This method requests modem to check if there are any pending datagrams to be received over
      * satellite. If there are any incoming datagrams, they will be received via
      * {@link SatelliteDatagramCallback#onSatelliteDatagramReceived(long, SatelliteDatagram, int,
-     *        ILongConsumer)}
+     * Consumer)} )}
      *
      * @param executor The executor on which the result listener will be called.
      * @param resultListener Listener for the {@link SatelliteError} result of the operation.
diff --git a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
index a780cb9..ea4e2e2 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
@@ -67,6 +67,15 @@
             in IIntegerConsumer resultCallback);
 
     /**
+     * Allow cellular modem scanning while satellite mode is on.
+     * @param enabled  {@code true} to enable cellular modem while satellite mode is on
+     * and {@code false} to disable
+     * @param errorCallback The callback to receive the error code result of the operation.
+     */
+    void enableCellularModemWhileSatelliteModeIsOn(in boolean enabled,
+        in IIntegerConsumer errorCallback);
+
+    /**
      * Request to enable or disable the satellite modem and demo mode. If the satellite modem
      * is enabled, this may also disable the cellular modem, and if the satellite modem is disabled,
      * this may also re-enable the cellular modem.
@@ -194,7 +203,7 @@
      *
      * @param token The token to be used as a unique identifier for provisioning with satellite
      *              gateway.
-     * @param regionId The region ID for the device's current location.
+     * @param provisionData Data from the provisioning app that can be used by provisioning server
      * @param resultCallback The callback to receive the error code result of the operation.
      *
      * Valid error codes returned:
@@ -210,7 +219,7 @@
      *   SatelliteError:REQUEST_ABORTED
      *   SatelliteError:NETWORK_TIMEOUT
      */
-    void provisionSatelliteService(in String token, in String regionId,
+    void provisionSatelliteService(in String token, in byte[] provisionData,
             in IIntegerConsumer resultCallback);
 
     /**
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
index debb394..d606f87 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
@@ -79,6 +79,15 @@
         }
 
         @Override
+        public void enableCellularModemWhileSatelliteModeIsOn(boolean enabled,
+                IIntegerConsumer errorCallback) throws RemoteException {
+            executeMethodAsync(
+                    () -> SatelliteImplBase.this
+                            .enableCellularModemWhileSatelliteModeIsOn(enabled, errorCallback),
+                    "enableCellularModemWhileSatelliteModeIsOn");
+        }
+
+        @Override
         public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode,
                 IIntegerConsumer errorCallback) throws RemoteException {
             executeMethodAsync(
@@ -132,11 +141,11 @@
         }
 
         @Override
-        public void provisionSatelliteService(String token, String regionId,
+        public void provisionSatelliteService(String token, byte[] provisionData,
                 IIntegerConsumer errorCallback) throws RemoteException {
             executeMethodAsync(
                     () -> SatelliteImplBase.this
-                            .provisionSatelliteService(token, regionId, errorCallback),
+                            .provisionSatelliteService(token, provisionData, errorCallback),
                     "provisionSatelliteService");
         }
 
@@ -261,6 +270,17 @@
     }
 
     /**
+     * Allow cellular modem scanning while satellite mode is on.
+     * @param enabled  {@code true} to enable cellular modem while satellite mode is on
+     * and {@code false} to disable
+     * @param errorCallback The callback to receive the error code result of the operation.
+     */
+    public void enableCellularModemWhileSatelliteModeIsOn(boolean enabled,
+            @NonNull IIntegerConsumer errorCallback) {
+        // stub implementation
+    }
+
+    /**
      * Request to enable or disable the satellite modem and demo mode. If the satellite modem is
      * enabled, this may also disable the cellular modem, and if the satellite modem is disabled,
      * this may also re-enable the cellular modem.
@@ -401,7 +421,8 @@
      *
      * @param token The token to be used as a unique identifier for provisioning with satellite
      *              gateway.
-     * @param regionId The region ID for the device's current location.
+     * @param provisionData Data from the provisioning app that can be used by provisioning 
+     *                      server
      * @param errorCallback The callback to receive the error code result of the operation.
      *
      * Valid error codes returned:
@@ -417,7 +438,7 @@
      *   SatelliteError:REQUEST_ABORTED
      *   SatelliteError:NETWORK_TIMEOUT
      */
-    public void provisionSatelliteService(@NonNull String token, @NonNull String regionId,
+    public void provisionSatelliteService(@NonNull String token, @NonNull byte[] provisionData,
             @NonNull IIntegerConsumer errorCallback) {
         // stub implementation
     }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index bab08b5..cbdf38ae 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2823,15 +2823,15 @@
      * @param subId The subId of the subscription to be provisioned.
      * @param token The token to be used as a unique identifier for provisioning with satellite
      *              gateway.
-     * @param regionId The region ID for the device's current location.
+     * @provisionData Data from the provisioning app that can be used by provisioning server
      * @param callback The callback to get the result of the request.
      *
      * @return The signal transport used by callers to cancel the provision request.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    ICancellationSignal provisionSatelliteService(int subId, in String token, in String regionId,
-            in IIntegerConsumer callback);
+    ICancellationSignal provisionSatelliteService(int subId, in String token,
+            in byte[] provisionData, in IIntegerConsumer callback);
 
     /**
      * Unregister the subscription with the satellite provider.
@@ -2980,4 +2980,13 @@
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
     void requestTimeForNextSatelliteVisibility(int subId, in ResultReceiver receiver);
+
+    /**
+     * This API can be used by only CTS to update satellite vendor service package name.
+     *
+     * @param servicePackageName The package name of the satellite vendor service.
+     * @return {@code true} if the satellite vendor service is set successfully,
+     * {@code false} otherwise.
+     */
+    boolean setSatelliteServicePackageName(in String servicePackageName);
 }
diff --git a/telephony/java/com/android/internal/telephony/IVoidConsumer.aidl b/telephony/java/com/android/internal/telephony/IVoidConsumer.aidl
new file mode 100644
index 0000000..b5557fd
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/IVoidConsumer.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ package com.android.internal.telephony;
+
+ /**
+  * Copies consumer pattern for an operation that requires void result from another process to
+  * finish.
+  */
+ oneway interface IVoidConsumer {
+    void accept();
+ }
\ No newline at end of file
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index fef5211..4ba538e 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -50,6 +50,9 @@
         "platform-test-annotations",
         "wm-flicker-window-extensions",
     ],
+    data: [
+        ":FlickerTestApp",
+    ],
 }
 
 java_library {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 9dc4bf0..314b9e4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -280,21 +280,28 @@
  *
  * @param originalLayer
  * ```
+ *
  * Layer that should be visible at the start
+ *
  * @param newLayer Layer that should be visible at the end
  * @param ignoreEntriesWithRotationLayer If entries with a visible rotation layer should be ignored
+ *
  * ```
  *      when checking the transition. If true we will not fail the assertion if a rotation layer is
  *      visible to fill the gap between the [originalLayer] being visible and the [newLayer] being
  *      visible.
  * @param ignoreSnapshot
  * ```
+ *
  * If the snapshot layer should be ignored during the transition
+ *
  * ```
  *     (useful mostly for app launch)
  * @param ignoreSplashscreen
  * ```
+ *
  * If the splashscreen layer should be ignored during the transition.
+ *
  * ```
  *      If true then we will allow for a splashscreen to be shown before the layer is shown,
  *      otherwise we won't and the layer must appear immediately.
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt
index 7aea05d..fde0981 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt
@@ -71,7 +71,7 @@
      * Open Assistance UI.
      *
      * @param longpress open the UI by long pressing power button. Otherwise open the UI through
-     * vioceinteraction shell command directly.
+     *   vioceinteraction shell command directly.
      */
     @JvmOverloads
     fun openUI(longpress: Boolean = false) {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
index 79c048a..d4f48fe 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
@@ -60,7 +60,6 @@
      *
      * @param wmHelper Helper used to get window region.
      * @param direction UiAutomator Direction enum to indicate the swipe direction.
-     *
      * @return true if the swipe operation is successful.
      */
     fun switchToPreviousAppByQuickSwitchGesture(
@@ -96,7 +95,6 @@
      * @param packageName The targe application's package name.
      * @param identifier The resource id of the target object.
      * @param timeout The timeout duration in milliseconds.
-     *
      * @return true if the target object exists.
      */
     @JvmOverloads
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index e497ae4..a72c12d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -57,18 +57,16 @@
         obj.click()
     }
 
-    /**
-     * Drags the PIP window to the provided final coordinates without releasing the pointer.
-     */
-    fun dragPipWindowAwayFromEdgeWithoutRelease(
-        wmHelper: WindowManagerStateHelper,
-        steps: Int
-    ) {
+    /** Drags the PIP window to the provided final coordinates without releasing the pointer. */
+    fun dragPipWindowAwayFromEdgeWithoutRelease(wmHelper: WindowManagerStateHelper, steps: Int) {
         val initWindowRect = getWindowRect(wmHelper).clone()
 
         // initial pointer at the center of the window
-        val initialCoord = GestureHelper.Tuple(initWindowRect.centerX().toFloat(),
-                initWindowRect.centerY().toFloat())
+        val initialCoord =
+            GestureHelper.Tuple(
+                initWindowRect.centerX().toFloat(),
+                initWindowRect.centerY().toFloat()
+            )
 
         // the offset to the right (or left) of the window center to drag the window to
         val offset = 50
@@ -76,8 +74,8 @@
         // the actual final x coordinate with the offset included;
         // if the pip window is closer to the right edge of the display the offset is negative
         // otherwise the offset is positive
-        val endX = initWindowRect.centerX() +
-            offset * (if (isCloserToRightEdge(wmHelper)) -1 else 1)
+        val endX =
+            initWindowRect.centerX() + offset * (if (isCloserToRightEdge(wmHelper)) -1 else 1)
         val finalCoord = GestureHelper.Tuple(endX.toFloat(), initWindowRect.centerY().toFloat())
 
         // drag to the final coordinate
@@ -106,7 +104,8 @@
         val startX = initWindowRect.centerX()
         val y = initWindowRect.centerY()
 
-        val displayRect = wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
+        val displayRect =
+            wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
                 ?: throw IllegalStateException("Default display is null")
 
         // the offset to the right (or left) of the display center to drag the window to
@@ -129,7 +128,8 @@
     fun isCloserToRightEdge(wmHelper: WindowManagerStateHelper): Boolean {
         val windowRect = getWindowRect(wmHelper)
 
-        val displayRect = wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
+        val displayRect =
+            wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
                 ?: throw IllegalStateException("Default display is null")
 
         return windowRect.centerX() > displayRect.centerX()
@@ -301,9 +301,7 @@
         closePipWindow(WindowManagerStateHelper(mInstrumentation))
     }
 
-    /**
-     * Returns the pip window bounds.
-     */
+    /** Returns the pip window bounds. */
     fun getWindowRect(wmHelper: WindowManagerStateHelper): Rect {
         val windowRegion = wmHelper.getWindowRegion(this)
         require(!windowRegion.isEmpty) { "Unable to find a PIP window in the current state" }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt
index 432df20..c355e27 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt
@@ -39,4 +39,4 @@
             )
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
index a4e4b6f..df9d33b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt
@@ -40,6 +40,7 @@
  *     Don't show if this is not explicitly requested by the user and the input method
  *     is fullscreen. That would be too disruptive.
  * ```
+ *
  * More details on b/190352379
  *
  * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToHomeTest`
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
index e85da1f..7954dd1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt
@@ -40,6 +40,7 @@
  *     Don't show if this is not explicitly requested by the user and the input method
  *     is fullscreen. That would be too disruptive.
  * ```
+ *
  * More details on b/190352379
  *
  * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToAppTest`
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
index 1fee20d..f97a038 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
@@ -19,7 +19,6 @@
 import android.platform.test.annotations.FlakyTest
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
-import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
 import android.tools.device.flicker.isShellTransitionsEnabled
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
@@ -27,6 +26,7 @@
 import android.tools.device.flicker.legacy.FlickerTestFactory
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
 import com.android.server.wm.flicker.helpers.setRotation
 import com.android.server.wm.flicker.snapshotStartingWindowLayerCoversExactlyOnApp
 import org.junit.Assume
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt
index efda0ff..e1aa418 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt
@@ -40,4 +40,4 @@
             )
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
index daee332..690ed53 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt
@@ -20,13 +20,13 @@
 import android.tools.common.NavBar
 import android.tools.common.Rotation
 import android.tools.common.datatypes.component.ComponentNameMatcher
-import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.FlickerTest
 import android.tools.device.flicker.legacy.FlickerTestFactory
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
 import com.android.server.wm.flicker.helpers.SimpleAppHelper
 import com.android.server.wm.flicker.helpers.setRotation
 import org.junit.FixMethodOrder
@@ -86,9 +86,7 @@
         }
     }
     /** {@inheritDoc} */
-    @Presubmit
-    @Test
-    override fun entireScreenCovered() = super.entireScreenCovered()
+    @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
 
     @Presubmit
     @Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
index 7514c9b..866e858 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt
@@ -19,14 +19,14 @@
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
 import android.tools.common.datatypes.component.ComponentNameMatcher
-import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
-import com.android.server.wm.flicker.helpers.ImeStateInitializeHelper
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.FlickerTest
 import android.tools.device.flicker.legacy.FlickerTestFactory
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
+import com.android.server.wm.flicker.helpers.ImeStateInitializeHelper
 import com.android.server.wm.flicker.helpers.setRotation
 import org.junit.FixMethodOrder
 import org.junit.Test
@@ -44,20 +44,27 @@
  *     Make sure no apps are running on the device
  *     Launch an app [testApp] that automatically displays IME and wait animation to complete
  * ```
+ *
  * To run only the presubmit assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
  *      --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
  * ```
+ *
  * To run only the postsubmit assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
  *      --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
  * ```
+ *
  * To run only the flaky assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
index a57aa5b..6f22589 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt
@@ -19,7 +19,6 @@
 import android.platform.test.annotations.Presubmit
 import android.tools.common.Rotation
 import android.tools.common.datatypes.component.ComponentNameMatcher
-import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.FlickerTest
@@ -29,6 +28,7 @@
 import android.view.WindowInsets.Type.statusBars
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
 import org.junit.Assert.assertFalse
 import org.junit.Assert.assertTrue
 import org.junit.FixMethodOrder
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt
index cffc05d..8891d26 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt
@@ -45,4 +45,4 @@
             )
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
index 9ea12a9..7135df6 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt
@@ -20,7 +20,6 @@
 import android.tools.common.datatypes.component.ComponentNameMatcher
 import android.tools.common.traces.ConditionsFactory
 import android.tools.device.flicker.isShellTransitionsEnabled
-import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
 import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.device.flicker.legacy.FlickerBuilder
 import android.tools.device.flicker.legacy.FlickerTest
@@ -28,6 +27,7 @@
 import android.tools.device.traces.parsers.WindowManagerStateHelper
 import androidx.test.filters.RequiresDevice
 import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
 import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd
 import com.android.server.wm.flicker.statusBarLayerIsVisibleAtStartAndEnd
 import org.junit.Assume
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
index e8f9aa3..3c577ac 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
@@ -44,6 +44,7 @@
  *     Launch a secondary activity within the app
  *     Close the secondary activity back to the initial one
  * ```
+ *
  * Notes:
  * ```
  *     1. Part of the test setup occurs automatically via
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
index 05abf9f..360a233 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
@@ -39,6 +39,7 @@
  *     Make sure no apps are running on the device
  *     Launch an app [testApp] by clicking it's icon on all apps and wait animation to complete
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
index 63ffee6..12c0874 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt
@@ -135,14 +135,15 @@
         }
 
         /**
-         * Ensures that posted notifications will be visible on the lockscreen and not
-         * suppressed due to being marked as seen.
+         * Ensures that posted notifications will be visible on the lockscreen and not suppressed
+         * due to being marked as seen.
          */
         @ClassRule
         @JvmField
-        val disableUnseenNotifFilterRule = SettingOverrideRule(
-            Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
-            /* value= */ "0",
-        )
+        val disableUnseenNotifFilterRule =
+            SettingOverrideRule(
+                Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
+                /* value= */ "0",
+            )
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
index a221ef6..222caed 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt
@@ -150,14 +150,15 @@
         }
 
         /**
-         * Ensures that posted notifications will be visible on the lockscreen and not
-         * suppressed due to being marked as seen.
+         * Ensures that posted notifications will be visible on the lockscreen and not suppressed
+         * due to being marked as seen.
          */
         @ClassRule
         @JvmField
-        val disableUnseenNotifFilterRule = SettingOverrideRule(
-            Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
-            /* value= */ "0",
-        )
+        val disableUnseenNotifFilterRule =
+            SettingOverrideRule(
+                Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
+                /* value= */ "0",
+            )
     }
 }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt
index d90b3ca..43d28fa 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt
@@ -42,4 +42,4 @@
             return FlickerTestFactory.nonRotationTests()
         }
     }
-}
\ No newline at end of file
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index 3fccd12..03f21e4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -221,9 +221,10 @@
                     .getIdentifier("image_wallpaper_component", "string", "android")
             // frameworks/base/core/res/res/values/config.xml returns package plus class name,
             // but wallpaper layer has only class name
-            val rawComponentMatcher = ComponentNameMatcher.unflattenFromString(
-                instrumentation.targetContext.resources.getString(resourceId)
-            )
+            val rawComponentMatcher =
+                ComponentNameMatcher.unflattenFromString(
+                    instrumentation.targetContext.resources.getString(resourceId)
+                )
 
             return ComponentNameMatcher(rawComponentMatcher.className)
         }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 4a4180b..fe789a7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -39,6 +39,7 @@
  *      0 -> 90 degrees
  *      90 -> 0 degrees
  * ```
+ *
  * Actions:
  * ```
  *     Launch an app (via intent)
@@ -47,22 +48,29 @@
  *     Change device orientation
  *     Stop tracing
  * ```
+ *
  * To run this test: `atest FlickerTests:ChangeAppRotationTest`
  *
  * To run only the presubmit assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
  *      --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
  * ```
+ *
  * To run only the postsubmit assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
  *      --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
  * ```
+ *
  * To run only the flaky assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 17b3b2b..4d010f3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -45,6 +45,7 @@
  *      90 -> 0 degrees
  *      90 -> 0 degrees (with starved UI thread)
  * ```
+ *
  * Actions:
  * ```
  *     Launch an app in fullscreen and supporting seamless rotation (via intent)
@@ -53,22 +54,29 @@
  *     Change device orientation
  *     Stop tracing
  * ```
+ *
  * To run this test: `atest FlickerTests:SeamlessAppRotationTest`
  *
  * To run only the presubmit assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
  *      --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
  * ```
+ *
  * To run only the postsubmit assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
  *      --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
  * ```
+ *
  * To run only the flaky assertions add: `--
+ *
  * ```
  *      --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
  * ```
+ *
  * Notes:
  * ```
  *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/tests/OdmApps/Android.bp b/tests/OdmApps/Android.bp
index de86498..5f03aa2 100644
--- a/tests/OdmApps/Android.bp
+++ b/tests/OdmApps/Android.bp
@@ -26,4 +26,7 @@
     srcs: ["src/**/*.java"],
     libs: ["tradefed"],
     test_suites: ["device-tests"],
+    data: [
+        ":TestOdmApp",
+    ],
 }
diff --git a/tests/SoundTriggerTests/Android.mk b/tests/SoundTriggerTests/Android.mk
deleted file mode 100644
index cc0fa1c..0000000
--- a/tests/SoundTriggerTests/Android.mk
+++ /dev/null
@@ -1,39 +0,0 @@
-#
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-ifeq ($(SOUND_TRIGGER_USE_STUB_MODULE), 1)
-  LOCAL_SRC_FILES := $(call all-subdir-java-files)
-  LOCAL_PRIVILEGED_MODULE := true
-  LOCAL_CERTIFICATE := platform
-  TARGET_OUT_DATA_APPS_PRIVILEGED := $(TARGET_OUT_DATA)/priv-app
-else
-  LOCAL_SRC_FILES := src/android/hardware/soundtrigger/SoundTriggerTest.java
-endif
-
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-
-LOCAL_PACKAGE_NAME := SoundTriggerTests
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE  := $(LOCAL_PATH)/../../NOTICE
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-include $(BUILD_PACKAGE)
diff --git a/tests/SoundTriggerTests/AndroidManifest.xml b/tests/SoundTriggerTests/AndroidManifest.xml
deleted file mode 100644
index f7454c7..0000000
--- a/tests/SoundTriggerTests/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="android.hardware.soundtrigger">
-    <uses-permission android:name="android.permission.MANAGE_SOUND_TRIGGER" />
-    <uses-permission android:name="android.permission.INTERNET" />
-    
-    <application>
-        <uses-library android:name="android.test.runner" />
-    </application>
-
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
-                     android:targetPackage="android.hardware.soundtrigger"
-                     android:label="Tests for android.hardware.soundtrigger" />
-</manifest>
diff --git a/tests/SoundTriggerTests/OWNERS b/tests/SoundTriggerTests/OWNERS
deleted file mode 100644
index 1e41886..0000000
--- a/tests/SoundTriggerTests/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /media/java/android/media/soundtrigger/OWNERS
diff --git a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java
deleted file mode 100644
index 2c3592c..0000000
--- a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.soundtrigger;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.hardware.soundtrigger.SoundTrigger.GenericRecognitionEvent;
-import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
-import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionEvent;
-import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
-import android.media.soundtrigger.SoundTriggerManager;
-import android.os.ParcelUuid;
-import android.os.ServiceManager;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.internal.app.ISoundTriggerService;
-
-import org.mockito.MockitoAnnotations;
-
-import java.io.DataOutputStream;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Random;
-import java.util.UUID;
-
-public class GenericSoundModelTest extends AndroidTestCase {
-    static final int MSG_DETECTION_ERROR = -1;
-    static final int MSG_DETECTION_RESUME = 0;
-    static final int MSG_DETECTION_PAUSE = 1;
-    static final int MSG_KEYPHRASE_TRIGGER = 2;
-    static final int MSG_GENERIC_TRIGGER = 4;
-
-    private Random random = new Random();
-    private HashSet<UUID> loadedModelUuids;
-    private ISoundTriggerService soundTriggerService;
-    private SoundTriggerManager soundTriggerManager;
-
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-        MockitoAnnotations.initMocks(this);
-
-        Context context = getContext();
-        soundTriggerService = ISoundTriggerService.Stub.asInterface(
-                ServiceManager.getService(Context.SOUND_TRIGGER_SERVICE));
-        soundTriggerManager = (SoundTriggerManager) context.getSystemService(
-                Context.SOUND_TRIGGER_SERVICE);
-
-        loadedModelUuids = new HashSet<UUID>();
-    }
-
-    @Override
-    public void tearDown() throws Exception {
-        for (UUID modelUuid : loadedModelUuids) {
-            soundTriggerService.deleteSoundModel(new ParcelUuid(modelUuid));
-        }
-        super.tearDown();
-    }
-
-    GenericSoundModel new_sound_model() {
-        // Create sound model
-        byte[] data = new byte[1024];
-        random.nextBytes(data);
-        UUID modelUuid = UUID.randomUUID();
-        UUID mVendorUuid = UUID.randomUUID();
-        return new GenericSoundModel(modelUuid, mVendorUuid, data);
-    }
-
-    @SmallTest
-    public void testUpdateGenericSoundModel() throws Exception {
-        GenericSoundModel model = new_sound_model();
-
-        // Update sound model
-        soundTriggerService.updateSoundModel(model);
-        loadedModelUuids.add(model.getUuid());
-
-        // Confirm it was updated
-        GenericSoundModel returnedModel =
-                soundTriggerService.getSoundModel(new ParcelUuid(model.getUuid()));
-        assertEquals(model, returnedModel);
-    }
-
-    @SmallTest
-    public void testDeleteGenericSoundModel() throws Exception {
-        GenericSoundModel model = new_sound_model();
-
-        // Update sound model
-        soundTriggerService.updateSoundModel(model);
-        loadedModelUuids.add(model.getUuid());
-
-        // Delete sound model
-        soundTriggerService.deleteSoundModel(new ParcelUuid(model.getUuid()));
-        loadedModelUuids.remove(model.getUuid());
-
-        // Confirm it was deleted
-        GenericSoundModel returnedModel =
-                soundTriggerService.getSoundModel(new ParcelUuid(model.getUuid()));
-        assertEquals(null, returnedModel);
-    }
-
-    @LargeTest
-    public void testStartStopGenericSoundModel() throws Exception {
-        GenericSoundModel model = new_sound_model();
-
-        boolean captureTriggerAudio = true;
-        boolean allowMultipleTriggers = true;
-        RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
-                null, null);
-        TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
-
-        // Update and start sound model recognition
-        soundTriggerService.updateSoundModel(model);
-        loadedModelUuids.add(model.getUuid());
-        int r = soundTriggerService.startRecognition(new ParcelUuid(model.getUuid()), spyCallback,
-                config);
-        assertEquals("Could Not Start Recognition with code: " + r,
-                android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
-
-        // Stop recognition
-        r = soundTriggerService.stopRecognition(new ParcelUuid(model.getUuid()), spyCallback);
-        assertEquals("Could Not Stop Recognition with code: " + r,
-                android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
-    }
-
-    @LargeTest
-    public void testTriggerGenericSoundModel() throws Exception {
-        GenericSoundModel model = new_sound_model();
-
-        boolean captureTriggerAudio = true;
-        boolean allowMultipleTriggers = true;
-        RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
-                null, null);
-        TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
-
-        // Update and start sound model
-        soundTriggerService.updateSoundModel(model);
-        loadedModelUuids.add(model.getUuid());
-        soundTriggerService.startRecognition(new ParcelUuid(model.getUuid()), spyCallback, config);
-
-        // Send trigger to stub HAL
-        Socket socket = new Socket(InetAddress.getLocalHost(), 14035);
-        DataOutputStream out = new DataOutputStream(socket.getOutputStream());
-        out.writeBytes("trig " + model.getUuid().toString() + "\r\n");
-        out.flush();
-        socket.close();
-
-        // Verify trigger was received
-        verify(spyCallback, timeout(100)).onGenericSoundTriggerDetected(any());
-    }
-
-    /**
-     * Tests a more complicated pattern of loading, unloading, triggering, starting and stopping
-     * recognition. Intended to find unexpected errors that occur in unexpected states.
-     */
-    @LargeTest
-    public void testFuzzGenericSoundModel() throws Exception {
-        int numModels = 2;
-
-        final int STATUS_UNLOADED = 0;
-        final int STATUS_LOADED = 1;
-        final int STATUS_STARTED = 2;
-
-        class ModelInfo {
-            int status;
-            GenericSoundModel model;
-
-            public ModelInfo(GenericSoundModel model, int status) {
-                this.status = status;
-                this.model = model;
-            }
-        }
-
-        Random predictableRandom = new Random(100);
-
-        ArrayList modelInfos = new ArrayList<ModelInfo>();
-        for(int i=0; i<numModels; i++) {
-            // Create sound model
-            byte[] data = new byte[1024];
-            predictableRandom.nextBytes(data);
-            UUID modelUuid = UUID.randomUUID();
-            UUID mVendorUuid = UUID.randomUUID();
-            GenericSoundModel model = new GenericSoundModel(modelUuid, mVendorUuid, data);
-            ModelInfo modelInfo = new ModelInfo(model, STATUS_UNLOADED);
-            modelInfos.add(modelInfo);
-        }
-
-        boolean captureTriggerAudio = true;
-        boolean allowMultipleTriggers = true;
-        RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
-                null, null);
-        TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
-
-
-        int numOperationsToRun = 100;
-        for(int i=0; i<numOperationsToRun; i++) {
-            // Select a random model
-            int modelInfoIndex = predictableRandom.nextInt(modelInfos.size());
-            ModelInfo modelInfo = (ModelInfo) modelInfos.get(modelInfoIndex);
-
-            // Perform a random operation
-            int operation = predictableRandom.nextInt(5);
-
-            if (operation == 0 && modelInfo.status == STATUS_UNLOADED) {
-                // Update and start sound model
-                soundTriggerService.updateSoundModel(modelInfo.model);
-                loadedModelUuids.add(modelInfo.model.getUuid());
-                modelInfo.status = STATUS_LOADED;
-            } else if (operation == 1 && modelInfo.status == STATUS_LOADED) {
-                // Start the sound model
-                int r = soundTriggerService.startRecognition(new ParcelUuid(
-                                modelInfo.model.getUuid()),
-                        spyCallback, config);
-                assertEquals("Could Not Start Recognition with code: " + r,
-                        android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
-                modelInfo.status = STATUS_STARTED;
-            } else if (operation == 2 && modelInfo.status == STATUS_STARTED) {
-                // Send trigger to stub HAL
-                Socket socket = new Socket(InetAddress.getLocalHost(), 14035);
-                DataOutputStream out = new DataOutputStream(socket.getOutputStream());
-                out.writeBytes("trig " + modelInfo.model.getUuid() + "\r\n");
-                out.flush();
-                socket.close();
-
-                // Verify trigger was received
-                verify(spyCallback, timeout(100)).onGenericSoundTriggerDetected(any());
-                reset(spyCallback);
-            } else if (operation == 3 && modelInfo.status == STATUS_STARTED) {
-                // Stop recognition
-                int r = soundTriggerService.stopRecognition(new ParcelUuid(
-                                modelInfo.model.getUuid()),
-                        spyCallback);
-                assertEquals("Could Not Stop Recognition with code: " + r,
-                        android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
-                modelInfo.status = STATUS_LOADED;
-            } else if (operation == 4 && modelInfo.status != STATUS_UNLOADED) {
-                // Delete sound model
-                soundTriggerService.deleteSoundModel(new ParcelUuid(modelInfo.model.getUuid()));
-                loadedModelUuids.remove(modelInfo.model.getUuid());
-
-                // Confirm it was deleted
-                GenericSoundModel returnedModel = soundTriggerService.getSoundModel(
-                        new ParcelUuid(modelInfo.model.getUuid()));
-                assertEquals(null, returnedModel);
-                modelInfo.status = STATUS_UNLOADED;
-            }
-        }
-    }
-
-    public class TestRecognitionStatusCallback extends IRecognitionStatusCallback.Stub {
-        @Override
-        public void onGenericSoundTriggerDetected(GenericRecognitionEvent recognitionEvent) {
-        }
-
-        @Override
-        public void onKeyphraseDetected(KeyphraseRecognitionEvent recognitionEvent) {
-        }
-
-        @Override
-        public void onError(int status) {
-        }
-
-        @Override
-        public void onRecognitionPaused() {
-        }
-
-        @Override
-        public void onRecognitionResumed() {
-        }
-    }
-}
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index ffde8c7..23efe54 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -55,6 +55,7 @@
         "cts-install-lib-host",
     ],
     data: [
+        ":StagedInstallInternalTestApp",
         ":apex.apexd_test",
         ":com.android.apex.apkrollback.test_v1",
         ":com.android.apex.apkrollback.test_v2",
diff --git a/tests/SystemMemoryTest/host/Android.bp b/tests/SystemMemoryTest/host/Android.bp
index 7974462..cc8bc45 100644
--- a/tests/SystemMemoryTest/host/Android.bp
+++ b/tests/SystemMemoryTest/host/Android.bp
@@ -26,4 +26,7 @@
     srcs: ["src/**/*.java"],
     libs: ["tradefed"],
     test_suites: ["general-tests"],
+    data: [
+        ":SystemMemoryTestDevice",
+    ],
 }