Merge "Fix BP did not dismiss after swiping up the notification bar" into udc-dev
diff --git a/apct-tests/perftests/blobstore/Android.bp b/apct-tests/perftests/blobstore/Android.bp
index 9064b44..2590fe3 100644
--- a/apct-tests/perftests/blobstore/Android.bp
+++ b/apct-tests/perftests/blobstore/Android.bp
@@ -29,7 +29,7 @@
         "androidx.test.rules",
         "androidx.annotation_annotation",
         "apct-perftests-utils",
-        "ub-uiautomator",
+        "androidx.test.uiautomator_uiautomator",
         "collector-device-lib-platform",
         "androidx.benchmark_benchmark-macro",
     ],
diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/AtraceUtils.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/AtraceUtils.java
index 0208dab..4e4780f 100644
--- a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/AtraceUtils.java
+++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/AtraceUtils.java
@@ -21,10 +21,10 @@
 import android.os.ParcelFileDescriptor;
 import android.perftests.utils.TraceMarkParser;
 import android.perftests.utils.TraceMarkParser.TraceMarkSlice;
-import android.support.test.uiautomator.UiDevice;
 import android.util.Log;
 
 import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
 
 import java.io.BufferedReader;
 import java.io.IOException;
diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
index 03e5468..3cd9f50 100644
--- a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
+++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
@@ -25,12 +25,12 @@
 import android.perftests.utils.PerfManualStatusReporter;
 import android.perftests.utils.TraceMarkParser;
 import android.perftests.utils.TraceMarkParser.TraceMarkSlice;
-import android.support.test.uiautomator.UiDevice;
 import android.util.DataUnit;
 
 import androidx.benchmark.macro.MacrobenchmarkScope;
 import androidx.test.filters.LargeTest;
 import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
 
 import com.android.utils.blob.FakeBlobData;
 
diff --git a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
index 64b2423..fd8ddbc 100644
--- a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
@@ -69,8 +69,8 @@
      * @return {@code true} if the given notification channel is associated with any user-initiated
      * jobs.
      */
-    boolean isNotificationChannelAssociatedWithAnyUserInitiatedJobs(String notificationChannel,
-            int userId, String packageName);
+    boolean isNotificationChannelAssociatedWithAnyUserInitiatedJobs(
+            @NonNull String notificationChannel, int userId, @NonNull String packageName);
 
     /**
      * Report a snapshot of sync-related jobs back to the sync manager
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 0650ce3..f779b4d 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -355,7 +355,7 @@
     @GuardedBy("this")
     private boolean mHasGps;
     @GuardedBy("this")
-    private boolean mHasNetworkLocation;
+    private boolean mHasFusedLocation;
     @GuardedBy("this")
     private Location mLastGenericLocation;
     @GuardedBy("this")
@@ -3782,12 +3782,14 @@
                 scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT);
                 LocationManager locationManager = mInjector.getLocationManager();
                 if (locationManager != null
-                        && locationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {
-                    locationManager.requestLocationUpdates(mLocationRequest,
-                            mGenericLocationListener, mHandler.getLooper());
+                        && locationManager.getProvider(LocationManager.FUSED_PROVIDER) != null) {
+                    locationManager.requestLocationUpdates(LocationManager.FUSED_PROVIDER,
+                            mLocationRequest,
+                            AppSchedulingModuleThread.getExecutor(),
+                            mGenericLocationListener);
                     mLocating = true;
                 } else {
-                    mHasNetworkLocation = false;
+                    mHasFusedLocation = false;
                 }
                 if (locationManager != null
                         && locationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
@@ -5301,9 +5303,10 @@
                 pw.print("  "); pw.print(mStationaryListeners.size());
                 pw.println(" stationary listeners registered");
             }
-            pw.print("  mLocating="); pw.print(mLocating); pw.print(" mHasGps=");
-                    pw.print(mHasGps); pw.print(" mHasNetwork=");
-                    pw.print(mHasNetworkLocation); pw.print(" mLocated="); pw.println(mLocated);
+            pw.print("  mLocating="); pw.print(mLocating);
+            pw.print(" mHasGps="); pw.print(mHasGps);
+            pw.print(" mHasFused="); pw.print(mHasFusedLocation);
+            pw.print(" mLocated="); pw.println(mLocated);
             if (mLastGenericLocation != null) {
                 pw.print("  mLastGenericLocation="); pw.println(mLastGenericLocation);
             }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index 8bd3d127..b9b825c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -1925,16 +1925,14 @@
         return null;
     }
 
-    @GuardedBy("mLock")
     boolean isNotificationAssociatedWithAnyUserInitiatedJobs(int notificationId, int userId,
-            String packageName) {
+            @NonNull String packageName) {
         return mNotificationCoordinator.isNotificationAssociatedWithAnyUserInitiatedJobs(
                 notificationId, userId, packageName);
     }
 
-    @GuardedBy("mLock")
-    boolean isNotificationChannelAssociatedWithAnyUserInitiatedJobs(String notificationChannel,
-            int userId, String packageName) {
+    boolean isNotificationChannelAssociatedWithAnyUserInitiatedJobs(
+            @NonNull String notificationChannel, int userId, @NonNull String packageName) {
         return mNotificationCoordinator.isNotificationChannelAssociatedWithAnyUserInitiatedJobs(
                 notificationChannel, userId, packageName);
     }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java b/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java
index f6e00ec..d94674b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java
@@ -27,9 +27,12 @@
 import android.os.UserHandle;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.IntArray;
 import android.util.Slog;
+import android.util.SparseArrayMap;
 import android.util.SparseSetArray;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.server.LocalServices;
 import com.android.server.job.controllers.JobStatus;
 import com.android.server.notification.NotificationManagerInternal;
@@ -38,6 +41,14 @@
     private static final String TAG = "JobNotificationCoordinator";
 
     /**
+     * Local lock for independent objects like mUijNotifications and mUijNotificationChannels which
+     * don't depend on other JS objects such as JobServiceContext which require the global JS lock.
+     *
+     * Note: do <b>NOT</b> acquire the global lock while this one is held.
+     */
+    private final Object mUijLock = new Object();
+
+    /**
      * Mapping of UserPackage -> {notificationId -> List<JobServiceContext>} to track which jobs
      * are associated with each app's notifications.
      */
@@ -49,6 +60,27 @@
     private final ArrayMap<JobServiceContext, NotificationDetails> mNotificationDetails =
             new ArrayMap<>();
 
+    /**
+     * Mapping of userId -> {packageName, notificationIds} tracking which notifications
+     * associated with each app belong to user-initiated jobs.
+     *
+     * Note: this map can be accessed without holding the main JS lock, so that other services like
+     * NotificationManagerService can call into JS and verify associations.
+     */
+    @GuardedBy("mUijLock")
+    private final SparseArrayMap<String, IntArray> mUijNotifications = new SparseArrayMap<>();
+
+    /**
+     * Mapping of userId -> {packageName, notificationChannels} tracking which notification channels
+     * associated with each app are hosting a user-initiated job notification.
+     *
+     * Note: this map can be accessed without holding the main JS lock, so that other services like
+     * NotificationManagerService can call into JS and verify associations.
+     */
+    @GuardedBy("mUijLock")
+    private final SparseArrayMap<String, ArraySet<String>> mUijNotificationChannels =
+            new SparseArrayMap<>();
+
     private static final class NotificationDetails {
         @NonNull
         public final UserPackage userPackage;
@@ -81,15 +113,24 @@
             int callingPid, int callingUid, int notificationId, @NonNull Notification notification,
             @JobService.JobEndNotificationPolicy int jobEndNotificationPolicy) {
         validateNotification(packageName, callingUid, notification, jobEndNotificationPolicy);
+        final JobStatus jobStatus = hostingContext.getRunningJobLocked();
         final NotificationDetails oldDetails = mNotificationDetails.get(hostingContext);
         if (oldDetails != null && oldDetails.notificationId != notificationId) {
             // App is switching notification IDs. Remove association with the old one.
-            removeNotificationAssociation(hostingContext, JobParameters.STOP_REASON_UNDEFINED);
+            removeNotificationAssociation(hostingContext, JobParameters.STOP_REASON_UNDEFINED,
+                    jobStatus);
         }
         final int userId = UserHandle.getUserId(callingUid);
-        final JobStatus jobStatus = hostingContext.getRunningJobLocked();
         if (jobStatus != null && jobStatus.startedAsUserInitiatedJob) {
             notification.flags |= Notification.FLAG_USER_INITIATED_JOB;
+            synchronized (mUijLock) {
+                maybeCreateUijNotificationSetsLocked(userId, packageName);
+                final IntArray notificationIds = mUijNotifications.get(userId, packageName);
+                if (notificationIds.indexOf(notificationId) == -1) {
+                    notificationIds.add(notificationId);
+                }
+                mUijNotificationChannels.get(userId, packageName).add(notification.getChannelId());
+            }
         }
         final UserPackage userPackage = UserPackage.of(userId, packageName);
         final NotificationDetails details = new NotificationDetails(
@@ -110,7 +151,7 @@
     }
 
     void removeNotificationAssociation(@NonNull JobServiceContext hostingContext,
-            @JobParameters.StopReason int stopReason) {
+            @JobParameters.StopReason int stopReason, JobStatus completedJob) {
         final NotificationDetails details = mNotificationDetails.remove(hostingContext);
         if (details == null) {
             return;
@@ -121,10 +162,11 @@
             Slog.wtf(TAG, "Association data structures not in sync");
             return;
         }
-        final String packageName = details.userPackage.packageName;
         final int userId = UserHandle.getUserId(details.appUid);
+        final String packageName = details.userPackage.packageName;
+        final int notificationId = details.notificationId;
         boolean stripUijFlag = true;
-        ArraySet<JobServiceContext> associatedContexts = associations.get(details.notificationId);
+        ArraySet<JobServiceContext> associatedContexts = associations.get(notificationId);
         if (associatedContexts == null || associatedContexts.isEmpty()) {
             // No more jobs using this notification. Apply the final job stop policy.
             // If the user attempted to stop the job/app, then always remove the notification
@@ -133,23 +175,50 @@
                     || stopReason == JobParameters.STOP_REASON_USER) {
                 mNotificationManagerInternal.cancelNotification(
                         packageName, packageName, details.appUid, details.appPid, /* tag */ null,
-                        details.notificationId, userId);
+                        notificationId, userId);
                 stripUijFlag = false;
             }
         } else {
             // Strip the UIJ flag only if there are no other UIJs associated with the notification
-            stripUijFlag = !isNotificationAssociatedWithAnyUserInitiatedJobs(
-                    details.notificationId, userId, packageName);
+            stripUijFlag = !isNotificationUsedForAnyUij(userId, packageName, notificationId);
         }
         if (stripUijFlag) {
-            // Strip the user-initiated job flag from the notification.
             mNotificationManagerInternal.removeUserInitiatedJobFlagFromNotification(
-                    packageName, details.notificationId, userId);
+                    packageName, notificationId, userId);
+        }
+
+        // Clean up UIJ related objects if the just completed job was a UIJ
+        if (completedJob != null && completedJob.startedAsUserInitiatedJob) {
+            maybeDeleteNotificationIdAssociation(userId, packageName, notificationId);
+            maybeDeleteNotificationChannelAssociation(
+                    userId, packageName, details.notificationChannel);
         }
     }
 
     boolean isNotificationAssociatedWithAnyUserInitiatedJobs(int notificationId,
-            int userId, String packageName) {
+            int userId, @NonNull String packageName) {
+        synchronized (mUijLock) {
+            final IntArray notifications = mUijNotifications.get(userId, packageName);
+            if (notifications != null) {
+                return notifications.indexOf(notificationId) != -1;
+            }
+            return false;
+        }
+    }
+
+    boolean isNotificationChannelAssociatedWithAnyUserInitiatedJobs(
+            @NonNull String notificationChannel, int userId, @NonNull String packageName) {
+        synchronized (mUijLock) {
+            final ArraySet<String> channels = mUijNotificationChannels.get(userId, packageName);
+            if (channels != null) {
+                return channels.contains(notificationChannel);
+            }
+            return false;
+        }
+    }
+
+    private boolean isNotificationUsedForAnyUij(int userId, String packageName,
+            int notificationId) {
         final UserPackage pkgDetails = UserPackage.of(userId, packageName);
         final SparseSetArray<JobServiceContext> associations = mCurrentAssociations.get(pkgDetails);
         if (associations == null) {
@@ -170,8 +239,26 @@
         return false;
     }
 
-    boolean isNotificationChannelAssociatedWithAnyUserInitiatedJobs(String notificationChannel,
-            int userId, String packageName) {
+    private void maybeDeleteNotificationIdAssociation(int userId, String packageName,
+            int notificationId) {
+        if (isNotificationUsedForAnyUij(userId, packageName, notificationId)) {
+            return;
+        }
+
+        // Safe to delete - no UIJs for this package are using this notification id
+        synchronized (mUijLock) {
+            final IntArray notifications = mUijNotifications.get(userId, packageName);
+            if (notifications != null) {
+                notifications.remove(notifications.indexOf(notificationId));
+                if (notifications.size() == 0) {
+                    mUijNotifications.delete(userId, packageName);
+                }
+            }
+        }
+    }
+
+    private void maybeDeleteNotificationChannelAssociation(int userId, String packageName,
+            String notificationChannel) {
         for (int i = mNotificationDetails.size() - 1; i >= 0; i--) {
             final JobServiceContext jsc = mNotificationDetails.keyAt(i);
             final NotificationDetails details = mNotificationDetails.get(jsc);
@@ -183,11 +270,31 @@
                     && details.notificationChannel.equals(notificationChannel)) {
                 final JobStatus jobStatus = jsc.getRunningJobLocked();
                 if (jobStatus != null && jobStatus.startedAsUserInitiatedJob) {
-                    return true;
+                    return;
                 }
             }
         }
-        return false;
+
+        // Safe to delete - no UIJs for this package are using this notification channel
+        synchronized (mUijLock) {
+            ArraySet<String> channels = mUijNotificationChannels.get(userId, packageName);
+            if (channels != null) {
+                channels.remove(notificationChannel);
+                if (channels.isEmpty()) {
+                    mUijNotificationChannels.delete(userId, packageName);
+                }
+            }
+        }
+    }
+
+    @GuardedBy("mUijLock")
+    private void maybeCreateUijNotificationSetsLocked(int userId, String packageName) {
+        if (mUijNotifications.get(userId, packageName) == null) {
+            mUijNotifications.add(userId, packageName, new IntArray());
+        }
+        if (mUijNotificationChannels.get(userId, packageName) == null) {
+            mUijNotificationChannels.add(userId, packageName, new ArraySet<>());
+        }
     }
 
     private void validateNotification(@NonNull String packageName, int callingUid,
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 3aec8ba..6eeff82 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -3713,26 +3713,22 @@
 
         @Override
         public boolean isNotificationAssociatedWithAnyUserInitiatedJobs(int notificationId,
-                int userId, String packageName) {
+                int userId, @NonNull String packageName) {
             if (packageName == null) {
                 return false;
             }
-            synchronized (mLock) {
-                return mConcurrencyManager.isNotificationAssociatedWithAnyUserInitiatedJobs(
-                        notificationId, userId, packageName);
-            }
+            return mConcurrencyManager.isNotificationAssociatedWithAnyUserInitiatedJobs(
+                    notificationId, userId, packageName);
         }
 
         @Override
         public boolean isNotificationChannelAssociatedWithAnyUserInitiatedJobs(
-                String notificationChannel, int userId, String packageName) {
+                @NonNull String notificationChannel, int userId, @NonNull String packageName) {
             if (packageName == null || notificationChannel == null) {
                 return false;
             }
-            synchronized (mLock) {
-                return mConcurrencyManager.isNotificationChannelAssociatedWithAnyUserInitiatedJobs(
-                        notificationChannel, userId, packageName);
-            }
+            return mConcurrencyManager.isNotificationChannelAssociatedWithAnyUserInitiatedJobs(
+                    notificationChannel, userId, packageName);
         }
 
         @Override
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 44700c8..fb36cde 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -1464,7 +1464,8 @@
                     JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT,
                     String.valueOf(mRunningJob.getJobId()));
         }
-        mNotificationCoordinator.removeNotificationAssociation(this, reschedulingStopReason);
+        mNotificationCoordinator.removeNotificationAssociation(this,
+                reschedulingStopReason, completedJob);
         if (mWakeLock != null) {
             mWakeLock.release();
         }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
index eb43c38..ef634b5 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
@@ -92,13 +92,17 @@
         final int priority = job.getEffectivePriority();
         if (mThermalStatus >= HIGHER_PRIORITY_THRESHOLD) {
             // For moderate throttling:
-            // Only let expedited & user-initiated jobs run if:
+            // Let all user-initiated jobs run.
+            // Only let expedited jobs run if:
             // 1. They haven't previously run
             // 2. They're already running and aren't yet in overtime
             // Only let high priority jobs run if:
             //   They are already running and aren't yet in overtime
             // Don't let any other job run.
-            if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) {
+            if (job.shouldTreatAsUserInitiatedJob()) {
+                return false;
+            }
+            if (job.shouldTreatAsExpeditedJob()) {
                 return job.getNumPreviousAttempts() > 0
                         || (mService.isCurrentlyRunningLocked(job)
                                 && mService.isJobInOvertimeLocked(job));
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 6998081..b6dc32a 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -21,6 +21,7 @@
 import android.app.backup.BackupManager;
 import android.app.backup.BackupManagerMonitor;
 import android.app.backup.BackupProgress;
+import android.app.backup.BackupRestoreEventLogger;
 import android.app.backup.BackupTransport;
 import android.app.backup.IBackupManager;
 import android.app.backup.IBackupManagerMonitor;
@@ -821,14 +822,22 @@
             doRestorePackage(arg);
         } else {
             try {
+                @Monitor int monitor = Monitor.OFF;
+
                 long token = Long.parseLong(arg, 16);
                 HashSet<String> filter = null;
                 while ((arg = nextArg()) != null) {
-                    if (filter == null) filter = new HashSet<String>();
-                    filter.add(arg);
+                    if (arg.equals("--monitor")) {
+                        monitor = Monitor.NORMAL;
+                    } else if (arg.equals("--monitor-verbose")) {
+                        monitor = Monitor.VERBOSE;
+                    } else {
+                        if (filter == null) filter = new HashSet<String>();
+                        filter.add(arg);
+                    }
                 }
 
-                doRestoreAll(userId, token, filter);
+                doRestoreAll(userId, token, filter, monitor);
             } catch (NumberFormatException e) {
                 showUsage();
                 return;
@@ -841,7 +850,8 @@
         System.err.println("'restore <token> <package>'.");
     }
 
-    private void doRestoreAll(@UserIdInt int userId, long token, HashSet<String> filter) {
+    private void doRestoreAll(@UserIdInt int userId, long token, HashSet<String> filter,
+            @Monitor int monitorState) {
         RestoreObserver observer = new RestoreObserver();
 
         try {
@@ -852,8 +862,11 @@
                 return;
             }
             RestoreSet[] sets = null;
-            // TODO implement monitor here
-            int err = mRestore.getAvailableRestoreSets(observer, null);
+            BackupMonitor monitor =
+                    (monitorState != Monitor.OFF)
+                            ? new BackupMonitor(monitorState == Monitor.VERBOSE)
+                            : null;
+            int err = mRestore.getAvailableRestoreSets(observer, monitor);
             if (err == 0) {
                 observer.waitForCompletion();
                 sets = observer.sets;
@@ -862,12 +875,12 @@
                         if (s.token == token) {
                             System.out.println("Scheduling restore: " + s.name);
                             if (filter == null) {
-                                didRestore = (mRestore.restoreAll(token, observer, null) == 0);
+                                didRestore = (mRestore.restoreAll(token, observer, monitor) == 0);
                             } else {
                                 String[] names = new String[filter.size()];
                                 filter.toArray(names);
                                 didRestore = (mRestore.restorePackages(token, observer, names,
-                                        null) == 0);
+                                        monitor) == 0);
                             }
                             break;
                         }
@@ -958,8 +971,8 @@
         System.err.println("       bmgr list transports [-c]");
         System.err.println("       bmgr list sets");
         System.err.println("       bmgr transport WHICH|-c WHICH_COMPONENT");
-        System.err.println("       bmgr restore TOKEN");
-        System.err.println("       bmgr restore TOKEN PACKAGE...");
+        System.err.println("       bmgr restore TOKEN [--monitor|--monitor-verbose]");
+        System.err.println("       bmgr restore TOKEN PACKAGE... [--monitor|--monitor-verbose]");
         System.err.println("       bmgr run");
         System.err.println("       bmgr wipe TRANSPORT PACKAGE");
         System.err.println("       bmgr fullbackup PACKAGE...");
@@ -1005,12 +1018,18 @@
         System.err.println("restore operation from the currently active transport.  It will deliver");
         System.err.println("the restore set designated by the TOKEN argument to each application");
         System.err.println("that had contributed data to that restore set.");
+        System.err.println("    --monitor flag prints monitor events (important events and errors");
+        System.err.println("              encountered during restore).");
+        System.err.println("    --monitor-verbose flag prints monitor events with all keys.");
         System.err.println("");
         System.err.println("The 'restore' command when given a token and one or more package names");
         System.err.println("initiates a restore operation of just those given packages from the restore");
         System.err.println("set designated by the TOKEN argument.  It is effectively the same as the");
         System.err.println("'restore' operation supplying only a token, but applies a filter to the");
         System.err.println("set of applications to be restored.");
+        System.err.println("    --monitor flag prints monitor events (important events and errors");
+        System.err.println("              encountered during restore).");
+        System.err.println("    --monitor-verbose flag prints monitor events with all keys.");
         System.err.println("");
         System.err.println("The 'run' command causes any scheduled backup operation to be initiated");
         System.err.println("immediately, without the usual waiting period for batching together");
@@ -1026,7 +1045,8 @@
         System.err.println("");
         System.err.println("The 'backupnow' command runs an immediate backup for one or more packages.");
         System.err.println("    --all flag runs backup for all eligible packages.");
-        System.err.println("    --monitor flag prints monitor events.");
+        System.err.println("    --monitor flag prints monitor events (important events and errors");
+        System.err.println("              encountered during backup).");
         System.err.println("    --monitor-verbose flag prints monitor events with all keys.");
         System.err.println("For each package it will run key/value or full data backup ");
         System.err.println("depending on the package's manifest declarations.");
@@ -1076,6 +1096,37 @@
                     out.append("(v").append(version).append(")");
                 }
             }
+            if (event.containsKey(BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS)) {
+                ArrayList<BackupRestoreEventLogger.DataTypeResult> results =
+                        event.getParcelableArrayList(
+                                BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS,
+                                BackupRestoreEventLogger.DataTypeResult.class);
+                out.append(", results = [");
+                for (BackupRestoreEventLogger.DataTypeResult result : results) {
+                    out.append("\n{\n\tdataType: ");
+                    out.append(result.getDataType());
+                    out.append("\n\tsuccessCount: ");
+                    out.append(result.getSuccessCount());
+                    out.append("\n\tfailCount: ");
+                    out.append(result.getFailCount());
+                    out.append("\n\tmetadataHash: ");
+                    out.append(Arrays.toString(result.getMetadataHash()));
+
+                    if (!result.getErrors().isEmpty()) {
+                        out.append("\n\terrors: [");
+                        for (String error : result.getErrors().keySet()) {
+                            out.append(error);
+                            out.append(": ");
+                            out.append(result.getErrors().get(error));
+                            out.append(";");
+                        }
+                        out.append("]");
+                    }
+                    out.append("\n}");
+
+                }
+                out.append("]");
+            }
             if (mVerbose) {
                 Set<String> remainingKeys = new ArraySet<>(event.keySet());
                 remainingKeys.remove(BackupManagerMonitor.EXTRA_LOG_EVENT_ID);
@@ -1083,6 +1134,7 @@
                 remainingKeys.remove(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME);
                 remainingKeys.remove(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION);
                 remainingKeys.remove(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_VERSION);
+                remainingKeys.remove(BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS);
                 if (!remainingKeys.isEmpty()) {
                     out.append(", other keys =");
                     for (String key : remainingKeys) {
@@ -1192,6 +1244,8 @@
                 return "NO_PACKAGES";
             case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL:
                 return "TRANSPORT_IS_NULL";
+            case BackupManagerMonitor.LOG_EVENT_ID_AGENT_LOGGING_RESULTS:
+                return "AGENT_LOGGING_RESULTS";
             default:
                 return "UNKNOWN_ID";
         }
diff --git a/core/java/android/inputmethodservice/InkWindow.java b/core/java/android/inputmethodservice/InkWindow.java
index 15ed450..24d1c95 100644
--- a/core/java/android/inputmethodservice/InkWindow.java
+++ b/core/java/android/inputmethodservice/InkWindow.java
@@ -195,6 +195,7 @@
         Objects.requireNonNull(decor);
         final ViewRootImpl viewRoot = decor.getViewRootImpl();
         Objects.requireNonNull(viewRoot);
-        viewRoot.enqueueInputEvent(event);
+        // The view root will own the event that we enqueue, so provide a copy of the event.
+        viewRoot.enqueueInputEvent(MotionEvent.obtain(event));
     }
 }
diff --git a/core/java/android/nfc/tech/NdefFormatable.java b/core/java/android/nfc/tech/NdefFormatable.java
index f19d302..2240fe7 100644
--- a/core/java/android/nfc/tech/NdefFormatable.java
+++ b/core/java/android/nfc/tech/NdefFormatable.java
@@ -124,6 +124,9 @@
         try {
             int serviceHandle = mTag.getServiceHandle();
             INfcTag tagService = mTag.getTagService();
+            if (tagService == null) {
+                throw new IOException();
+            }
             int errorCode = tagService.formatNdef(serviceHandle, MifareClassic.KEY_DEFAULT);
             switch (errorCode) {
                 case ErrorCodes.SUCCESS:
diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java
index 63259ed..218ecc8 100644
--- a/core/java/android/os/image/DynamicSystemClient.java
+++ b/core/java/android/os/image/DynamicSystemClient.java
@@ -202,6 +202,13 @@
     public static final String ACTION_NOTIFY_IF_IN_USE =
             "android.os.image.action.NOTIFY_IF_IN_USE";
 
+    /**
+     * Intent action: hide notifications about the status of {@code DynamicSystem}.
+     * @hide
+     */
+    public static final String ACTION_HIDE_NOTIFICATION =
+            "android.os.image.action.HIDE_NOTIFICATION";
+
     /*
      * Intent Keys
      */
@@ -217,6 +224,28 @@
      */
     public static final String KEY_USERDATA_SIZE = "KEY_USERDATA_SIZE";
 
+    /**
+     * Intent key: Whether to enable DynamicSystem immediately after installation is done.
+     *             Note this will reboot the device automatically.
+     * @hide
+     */
+    public static final String KEY_ENABLE_WHEN_COMPLETED = "KEY_ENABLE_WHEN_COMPLETED";
+
+    /**
+     * Intent key: Whether to leave DynamicSystem on device reboot.
+     *             False indicates a sticky mode where device stays in DynamicSystem across reboots.
+     * @hide
+     */
+    public static final String KEY_ONE_SHOT = "KEY_ONE_SHOT";
+
+    /**
+     * Intent key: Whether to use default strings when showing the dialog that prompts
+     *             user for device credentials.
+     *             False indicates using the custom strings provided by {@code DynamicSystem}.
+     * @hide
+     */
+    public static final String KEY_KEYGUARD_USE_DEFAULT_STRINGS =
+            "KEY_KEYGUARD_USE_DEFAULT_STRINGS";
 
     private static class IncomingHandler extends Handler {
         private final WeakReference<DynamicSystemClient> mWeakClient;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7cb959d..aa5a0d0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2585,7 +2585,7 @@
      * <p>
      * To start an activity with this intent, apps should set the wellbeing package explicitly in
      * the intent together with this action. The wellbeing package is defined in
-     * {@code com.android.internal.R.string.config_defaultWellbeingPackage}.
+     * {@code com.android.internal.R.string.config_systemWellbeing}.
      * <p>
      * Output: Nothing
      *
diff --git a/core/java/android/service/credentials/CredentialProviderInfoFactory.java b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
index d2a4a66..fb2f4ad 100644
--- a/core/java/android/service/credentials/CredentialProviderInfoFactory.java
+++ b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
@@ -42,7 +42,6 @@
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.AttributeSet;
-import android.util.Log;
 import android.util.Slog;
 import android.util.Xml;
 
@@ -54,7 +53,6 @@
 
 import java.io.IOException;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -181,7 +179,8 @@
         if (disableSystemAppVerificationForTests) {
             Bundle metadata = serviceInfo.metaData;
             if (metadata == null) {
-                Slog.e(TAG, "isValidSystemProvider - metadata is null: " + serviceInfo);
+                Slog.w(TAG, "metadata is null while reading "
+                        + "TEST_SYSTEM_PROVIDER_META_DATA_KEY: " + serviceInfo);
                 return false;
             }
             return metadata.getBoolean(
@@ -200,7 +199,7 @@
         // 1. Get the metadata for the service.
         final Bundle metadata = serviceInfo.metaData;
         if (metadata == null) {
-            Log.i(TAG, "populateMetadata - metadata is null");
+            Slog.w(TAG, "Metadata is null for provider: " + serviceInfo.getComponentName());
             return builder;
         }
 
@@ -209,12 +208,13 @@
         try {
             resources = pm.getResourcesForApplication(serviceInfo.applicationInfo);
         } catch (PackageManager.NameNotFoundException e) {
-            Log.e(TAG, "Failed to get app resources", e);
+            Slog.e(TAG, "Failed to get app resources", e);
         }
 
         // 3. Stop if we are missing data.
-        if (metadata == null || resources == null) {
-            Log.i(TAG, "populateMetadata - resources is null");
+        if (resources == null) {
+            Slog.w(TAG, "Resources are null for the serviceInfo being processed: "
+                    + serviceInfo.getComponentName());
             return builder;
         }
 
@@ -222,7 +222,7 @@
         try {
             builder = extractXmlMetadata(context, builder, serviceInfo, pm, resources);
         } catch (Exception e) {
-            Log.e(TAG, "Failed to get XML metadata", e);
+            Slog.e(TAG, "Failed to get XML metadata", e);
         }
 
         return builder;
@@ -259,7 +259,7 @@
                             afsAttributes.getString(
                                     R.styleable.CredentialProvider_settingsSubtitle));
                 } catch (Exception e) {
-                    Log.e(TAG, "Failed to get XML attr", e);
+                    Slog.e(TAG, "Failed to get XML attr", e);
                 } finally {
                     if (afsAttributes != null) {
                         afsAttributes.recycle();
@@ -267,10 +267,10 @@
                 }
                 builder.addCapabilities(parseXmlProviderOuterCapabilities(parser, resources));
             } else {
-                Log.e(TAG, "Meta-data does not start with credential-provider-service tag");
+                Slog.w(TAG, "Meta-data does not start with credential-provider-service tag");
             }
         } catch (IOException | XmlPullParserException e) {
-            Log.e(TAG, "Error parsing credential provider service meta-data", e);
+            Slog.e(TAG, "Error parsing credential provider service meta-data", e);
         }
 
         return builder;
@@ -329,7 +329,7 @@
                 return si;
             }
         } catch (RemoteException e) {
-            Slog.v(TAG, e.getMessage());
+            Slog.e(TAG, "Unable to get serviceInfo", e);
         }
         throw new PackageManager.NameNotFoundException(serviceComponent.toString());
     }
@@ -377,10 +377,8 @@
                 }
 
                 services.add(serviceInfo);
-            } catch (SecurityException e) {
-                Slog.e(TAG, "Error getting info for " + serviceInfo + ": " + e);
-            } catch (PackageManager.NameNotFoundException e) {
-                Slog.e(TAG, "Error getting info for " + serviceInfo + ": " + e);
+            } catch (SecurityException | PackageManager.NameNotFoundException e) {
+                Slog.e(TAG, "Error getting info for " + serviceInfo, e);
             }
         }
         return services;
@@ -432,7 +430,7 @@
             return pp;
         } catch (SecurityException e) {
             // If the current user is not enrolled in DPM then this can throw a security error.
-            Log.e(TAG, "Failed to get device policy: " + e);
+            Slog.e(TAG, "Failed to get device policy: " + e);
         }
 
         return null;
@@ -593,7 +591,7 @@
         for (ResolveInfo resolveInfo : resolveInfos) {
             final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
             if (serviceInfo == null) {
-                Log.i(TAG, "No serviceInfo found for resolveInfo so skipping this provider");
+                Slog.d(TAG, "No serviceInfo found for resolveInfo, so skipping provider");
                 continue;
             }
 
@@ -608,10 +606,8 @@
                 if (!cpi.isSystemProvider()) {
                     services.add(cpi);
                 }
-            } catch (SecurityException e) {
-                Slog.e(TAG, "Error getting info for " + serviceInfo + ": " + e);
             } catch (Exception e) {
-                Slog.e(TAG, "Error getting info for " + serviceInfo + ": " + e);
+                Slog.e(TAG, "Error getting info for " + serviceInfo, e);
             }
         }
         return services;
diff --git a/core/java/android/service/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java
index e87333f..53a5fd5 100644
--- a/core/java/android/service/credentials/CredentialProviderService.java
+++ b/core/java/android/service/credentials/CredentialProviderService.java
@@ -226,7 +226,7 @@
         if (SERVICE_INTERFACE.equals(intent.getAction())) {
             return mInterface.asBinder();
         }
-        Log.i(TAG, "Failed to bind with intent: " + intent);
+        Log.d(TAG, "Failed to bind with intent: " + intent);
         return null;
     }
 
diff --git a/core/java/android/service/dreams/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java
index dd5373f..82571db 100644
--- a/core/java/android/service/dreams/DreamManagerInternal.java
+++ b/core/java/android/service/dreams/DreamManagerInternal.java
@@ -80,10 +80,10 @@
      */
     public interface DreamManagerStateListener {
         /**
-         * Called when keep dreaming when undocked has changed.
+         * Called when keep dreaming when plug has changed.
          *
          * @param keepDreaming True if the current dream should continue when undocking.
          */
-        void onKeepDreamingWhenUndockedChanged(boolean keepDreaming);
+        void onKeepDreamingWhenUnpluggingChanged(boolean keepDreaming);
     }
 }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 77bbeb5..74ab709 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -112,6 +112,7 @@
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Supplier;
 
 /**
@@ -431,6 +432,7 @@
                 Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED,
                         reportDraw ? 1 : 0,
                         mergedConfiguration);
+                mIWallpaperEngine.mPendingResizeCount.incrementAndGet();
                 mCaller.sendMessage(msg);
             }
 
@@ -510,6 +512,7 @@
         public Engine(Supplier<Long> clockFunction, Handler handler) {
             mClockFunction = clockFunction;
             mHandler = handler;
+            mMergedConfiguration.setOverrideConfiguration(getResources().getConfiguration());
         }
 
         /**
@@ -1051,6 +1054,10 @@
             out.print(prefix); out.print("mZoom="); out.println(mZoom);
             out.print(prefix); out.print("mPreviewSurfacePosition=");
                     out.println(mPreviewSurfacePosition);
+            final int pendingCount = mIWallpaperEngine.mPendingResizeCount.get();
+            if (pendingCount != 0) {
+                out.print(prefix); out.print("mPendingResizeCount="); out.println(pendingCount);
+            }
             synchronized (mLock) {
                 out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset);
                         out.print(" mPendingXOffset="); out.println(mPendingXOffset);
@@ -1113,10 +1120,6 @@
             }
         }
 
-        private void updateConfiguration(MergedConfiguration mergedConfiguration) {
-            mMergedConfiguration.setTo(mergedConfiguration);
-        }
-
         void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) {
             if (mDestroyed) {
                 Log.w(TAG, "Ignoring updateSurface due to destroyed");
@@ -1165,7 +1168,7 @@
                             | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 
                     final Configuration config = mMergedConfiguration.getMergedConfiguration();
-                    final Rect maxBounds = config.windowConfiguration.getMaxBounds();
+                    final Rect maxBounds = new Rect(config.windowConfiguration.getMaxBounds());
                     if (myWidth == ViewGroup.LayoutParams.MATCH_PARENT
                             && myHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
                         mLayout.width = myWidth;
@@ -1221,6 +1224,17 @@
                     final int relayoutResult = mSession.relayout(mWindow, mLayout, mWidth, mHeight,
                             View.VISIBLE, 0, 0, 0, mWinFrames, mMergedConfiguration,
                             mSurfaceControl, mInsetsState, mTempControls, mSyncSeqIdBundle);
+                    final Rect outMaxBounds = mMergedConfiguration.getMergedConfiguration()
+                            .windowConfiguration.getMaxBounds();
+                    if (!outMaxBounds.equals(maxBounds)) {
+                        Log.i(TAG, "Retry updateSurface because bounds changed from relayout: "
+                                + maxBounds + " -> " + outMaxBounds);
+                        mSurfaceHolder.mSurfaceLock.unlock();
+                        mDrawingAllowed = false;
+                        mCaller.sendMessage(mCaller.obtainMessageI(MSG_WINDOW_RESIZED,
+                                redrawNeeded ? 1 : 0));
+                        return;
+                    }
 
                     final int transformHint = SurfaceControl.rotationToBufferTransform(
                             (mDisplay.getInstallOrientation() + mDisplay.getRotation()) % 4);
@@ -2324,6 +2338,8 @@
         final IBinder mWindowToken;
         final int mWindowType;
         final boolean mIsPreview;
+        final AtomicInteger mPendingResizeCount = new AtomicInteger();
+        boolean mReportDraw;
         boolean mShownReported;
         int mReqWidth;
         int mReqHeight;
@@ -2579,11 +2595,7 @@
                     mEngine.doCommand(cmd);
                 } break;
                 case MSG_WINDOW_RESIZED: {
-                    final boolean reportDraw = message.arg1 != 0;
-                    mEngine.updateConfiguration(((MergedConfiguration) message.obj));
-                    mEngine.updateSurface(true, false, reportDraw);
-                    mEngine.doOffsetsChanged(true);
-                    mEngine.scaleAndCropScreenshot();
+                    handleResized((MergedConfiguration) message.obj, message.arg1 != 0);
                 } break;
                 case MSG_WINDOW_MOVED: {
                     // Do nothing. What does it mean for a Wallpaper to move?
@@ -2631,6 +2643,40 @@
                     Log.w(TAG, "Unknown message type " + message.what);
             }
         }
+
+        /**
+         * In general this performs relayout for IWindow#resized. If there are several pending
+         * (in the message queue) MSG_WINDOW_RESIZED from server side, only the last one will be
+         * handled (ignore intermediate states). Note that this procedure cannot be skipped if the
+         * configuration is not changed because this is also used to dispatch insets changes.
+         */
+        private void handleResized(MergedConfiguration config, boolean reportDraw) {
+            // The config can be null when retrying for a changed config from relayout, otherwise
+            // it is from IWindow#resized which always sends non-null config.
+            final int pendingCount = config != null ? mPendingResizeCount.decrementAndGet() : -1;
+            if (reportDraw) {
+                mReportDraw = true;
+            }
+            if (pendingCount > 0) {
+                if (DEBUG) {
+                    Log.d(TAG, "Skip outdated resize, bounds="
+                            + config.getMergedConfiguration().windowConfiguration.getMaxBounds()
+                            + " pendingCount=" + pendingCount);
+                }
+                return;
+            }
+            if (config != null) {
+                if (DEBUG) {
+                    Log.d(TAG, "Update config from resized, bounds="
+                            + config.getMergedConfiguration().windowConfiguration.getMaxBounds());
+                }
+                mEngine.mMergedConfiguration.setTo(config);
+            }
+            mEngine.updateSurface(true /* forceRelayout */, false /* forceReport */, mReportDraw);
+            mReportDraw = false;
+            mEngine.doOffsetsChanged(true);
+            mEngine.scaleAndCropScreenshot();
+        }
     }
 
     /**
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 5dd2d82..edc5993 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -195,7 +195,7 @@
 
     private boolean mDebugPrintNextFrameTimeDelta;
     private int mFPSDivisor = 1;
-    private DisplayEventReceiver.VsyncEventData mLastVsyncEventData =
+    private final DisplayEventReceiver.VsyncEventData mLastVsyncEventData =
             new DisplayEventReceiver.VsyncEventData();
     private final FrameData mFrameData = new FrameData();
 
@@ -860,7 +860,7 @@
                 mFrameScheduled = false;
                 mLastFrameTimeNanos = frameTimeNanos;
                 mLastFrameIntervalNanos = frameIntervalNanos;
-                mLastVsyncEventData = vsyncEventData;
+                mLastVsyncEventData.copyFrom(vsyncEventData);
             }
 
             if (resynced && Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
@@ -1262,7 +1262,7 @@
         private boolean mHavePendingVsync;
         private long mTimestampNanos;
         private int mFrame;
-        private VsyncEventData mLastVsyncEventData = new VsyncEventData();
+        private final VsyncEventData mLastVsyncEventData = new VsyncEventData();
 
         FrameDisplayEventReceiver(Looper looper, int vsyncSource, long layerHandle) {
             super(looper, vsyncSource, /* eventRegistration */ 0, layerHandle);
@@ -1302,7 +1302,7 @@
 
                 mTimestampNanos = timestampNanos;
                 mFrame = frame;
-                mLastVsyncEventData = vsyncEventData;
+                mLastVsyncEventData.copyFrom(vsyncEventData);
                 Message msg = Message.obtain(mHandler, this);
                 msg.setAsynchronous(true);
                 mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 0307489..54db34e 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -81,7 +81,10 @@
     // GC'd while the native peer of the receiver is using them.
     private MessageQueue mMessageQueue;
 
+    private final VsyncEventData mVsyncEventData = new VsyncEventData();
+
     private static native long nativeInit(WeakReference<DisplayEventReceiver> receiver,
+            WeakReference<VsyncEventData> vsyncEventData,
             MessageQueue messageQueue, int vsyncSource, int eventRegistration, long layerHandle);
     private static native long nativeGetDisplayEventReceiverFinalizer();
     @FastNative
@@ -124,7 +127,9 @@
         }
 
         mMessageQueue = looper.getQueue();
-        mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
+        mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this),
+                new WeakReference<VsyncEventData>(mVsyncEventData),
+                mMessageQueue,
                 vsyncSource, eventRegistration, layerHandle);
         mFreeNativeResources = sNativeAllocationRegistry.registerNativeAllocation(this,
                 mReceiverPtr);
@@ -147,9 +152,6 @@
      * @hide
      */
     public static final class VsyncEventData {
-        static final FrameTimeline[] INVALID_FRAME_TIMELINES =
-                {new FrameTimeline(FrameInfo.INVALID_VSYNC_ID, Long.MAX_VALUE, Long.MAX_VALUE)};
-
         // The amount of frame timeline choices.
         // Must be in sync with VsyncEventData::kFrameTimelinesLength in
         // frameworks/native/libs/gui/include/gui/VsyncEventData.h. If they do not match, a runtime
@@ -157,22 +159,32 @@
         static final int FRAME_TIMELINES_LENGTH = 7;
 
         public static class FrameTimeline {
+            FrameTimeline() {}
+
+            // Called from native code.
+            @SuppressWarnings("unused")
             FrameTimeline(long vsyncId, long expectedPresentationTime, long deadline) {
                 this.vsyncId = vsyncId;
                 this.expectedPresentationTime = expectedPresentationTime;
                 this.deadline = deadline;
             }
 
+            void copyFrom(FrameTimeline other) {
+                vsyncId = other.vsyncId;
+                expectedPresentationTime = other.expectedPresentationTime;
+                deadline = other.deadline;
+            }
+
             // The frame timeline vsync id, used to correlate a frame
             // produced by HWUI with the timeline data stored in Surface Flinger.
-            public final long vsyncId;
+            public long vsyncId = FrameInfo.INVALID_VSYNC_ID;
 
             // The frame timestamp for when the frame is expected to be presented.
-            public final long expectedPresentationTime;
+            public long expectedPresentationTime = Long.MAX_VALUE;
 
             // The frame deadline timestamp in {@link System#nanoTime()} timebase that it is
             // allotted for the frame to be completed.
-            public final long deadline;
+            public long deadline = Long.MAX_VALUE;
         }
 
         /**
@@ -180,11 +192,18 @@
          * {@link FrameInfo#VSYNC} to the current vsync in case Choreographer callback was heavily
          * delayed by the app.
          */
-        public final long frameInterval;
+        public long frameInterval = -1;
 
         public final FrameTimeline[] frameTimelines;
 
-        public final int preferredFrameTimelineIndex;
+        public int preferredFrameTimelineIndex = 0;
+
+        VsyncEventData() {
+            frameTimelines = new FrameTimeline[FRAME_TIMELINES_LENGTH];
+            for (int i = 0; i < frameTimelines.length; i++) {
+                frameTimelines[i] = new FrameTimeline();
+            }
+        }
 
         // Called from native code.
         @SuppressWarnings("unused")
@@ -195,10 +214,12 @@
             this.frameInterval = frameInterval;
         }
 
-        VsyncEventData() {
-            this.frameInterval = -1;
-            this.frameTimelines = INVALID_FRAME_TIMELINES;
-            this.preferredFrameTimelineIndex = 0;
+        void copyFrom(VsyncEventData other) {
+            preferredFrameTimelineIndex = other.preferredFrameTimelineIndex;
+            frameInterval = other.frameInterval;
+            for (int i = 0; i < frameTimelines.length; i++) {
+                frameTimelines[i].copyFrom(other.frameTimelines[i]);
+            }
         }
 
         public FrameTimeline preferredFrameTimeline() {
@@ -304,9 +325,8 @@
 
     // Called from native code.
     @SuppressWarnings("unused")
-    private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame,
-            VsyncEventData vsyncEventData) {
-        onVsync(timestampNanos, physicalDisplayId, frame, vsyncEventData);
+    private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
+        onVsync(timestampNanos, physicalDisplayId, frame, mVsyncEventData);
     }
 
     // Called from native code.
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index e9984da..bd6224b 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -120,6 +120,8 @@
 
     private ISurfaceControlViewHost mRemoteInterface = new ISurfaceControlViewHostImpl();
 
+    private ViewRootImpl.ConfigChangedCallback mConfigChangedCallback;
+
     /**
      * Package encapsulating a Surface hierarchy which contains interactive view
      * elements. It's expected to get this object from
@@ -307,7 +309,7 @@
         mWm = wwm;
         mViewRoot = new ViewRootImpl(c, d, mWm, new WindowlessWindowLayout());
         mCloseGuard.openWithCallSite("release", callsite);
-        addConfigCallback(c, d);
+        setConfigCallback(c, d);
 
         WindowManagerGlobal.getInstance().addWindowlessRoot(mViewRoot);
 
@@ -357,21 +359,23 @@
 
         mViewRoot = new ViewRootImpl(context, display, mWm, new WindowlessWindowLayout());
         mCloseGuard.openWithCallSite("release", callsite);
-        addConfigCallback(context, display);
+        setConfigCallback(context, display);
 
         WindowManagerGlobal.getInstance().addWindowlessRoot(mViewRoot);
 
         mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
     }
 
-    private void addConfigCallback(Context c, Display d) {
+    private void setConfigCallback(Context c, Display d) {
         final IBinder token = c.getWindowContextToken();
-        mViewRoot.addConfigCallback((conf) -> {
+        mConfigChangedCallback = conf -> {
             if (token instanceof WindowTokenClient) {
                 final WindowTokenClient w = (WindowTokenClient)  token;
                 w.onConfigurationChanged(conf, d.getDisplayId(), true);
             }
-        });
+        };
+
+        ViewRootImpl.addConfigCallback(mConfigChangedCallback);
     }
 
     /**
@@ -386,8 +390,7 @@
             mCloseGuard.warnIfOpen();
         }
         // We aren't on the UI thread here so we need to pass false to doDie
-        mViewRoot.die(false /* immediate */);
-        WindowManagerGlobal.getInstance().removeWindowlessRoot(mViewRoot);
+        doRelease(false /* immediate */);
     }
 
     /**
@@ -400,8 +403,7 @@
     public @Nullable SurfacePackage getSurfacePackage() {
         if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) {
             return new SurfacePackage(new SurfaceControl(mSurfaceControl, "getSurfacePackage"),
-                mAccessibilityEmbeddedConnection,
-                mWm.getFocusGrantToken(), mRemoteInterface);
+                mAccessibilityEmbeddedConnection, getFocusGrantToken(), mRemoteInterface);
         } else {
             return null;
         }
@@ -497,7 +499,16 @@
      */
     public void release() {
         // ViewRoot will release mSurfaceControl for us.
-        mViewRoot.die(true /* immediate */);
+        doRelease(true /* immediate */);
+    }
+
+    private void doRelease(boolean immediate) {
+        if (mConfigChangedCallback != null) {
+            ViewRootImpl.removeConfigCallback(mConfigChangedCallback);
+            mConfigChangedCallback = null;
+        }
+
+        mViewRoot.die(immediate);
         WindowManagerGlobal.getInstance().removeWindowlessRoot(mViewRoot);
         mReleased = true;
         mCloseGuard.close();
@@ -507,7 +518,7 @@
      * @hide
      */
     public IBinder getFocusGrantToken() {
-        return mWm.getFocusGrantToken();
+        return mWm.getFocusGrantToken(getWindowToken().asBinder());
     }
 
     private void addWindowToken(WindowManager.LayoutParams attrs) {
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 9868144..96bfb2d 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -106,8 +106,22 @@
         mConfiguration.setTo(configuration);
     }
 
-    IBinder getFocusGrantToken() {
-        return mFocusGrantToken;
+    IBinder getFocusGrantToken(IBinder window) {
+        synchronized (this) {
+            // This can only happen if someone requested the focusGrantToken before setView was
+            // called for the SCVH. In that case, use the root focusGrantToken since this will be
+            // the same token sent to WMS for the root window once setView is called.
+            if (mStateForWindow.isEmpty()) {
+                return mFocusGrantToken;
+            }
+            State state = mStateForWindow.get(window);
+            if (state != null) {
+                return state.mFocusGrantToken;
+            }
+        }
+
+        Log.w(TAG, "Failed to get focusGrantToken. Returning null token");
+        return null;
     }
 
     /**
diff --git a/core/java/android/view/translation/Translator.java b/core/java/android/view/translation/Translator.java
index 70db6e5..50249da 100644
--- a/core/java/android/view/translation/Translator.java
+++ b/core/java/android/view/translation/Translator.java
@@ -18,7 +18,6 @@
 
 import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_FAIL;
 import static android.view.translation.TranslationManager.SYNC_CALLS_TIMEOUT_MS;
-import static android.view.translation.UiTranslationController.DEBUG;
 
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
@@ -402,7 +401,7 @@
 
         @Override
         public void onTranslationResponse(TranslationResponse response) throws RemoteException {
-            if (DEBUG) {
+            if (Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG)) {
                 Log.i(TAG, "onTranslationResponse called.");
             }
             final Runnable runnable =
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 514df59..140e3f1 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -122,8 +122,9 @@
             Log.i(TAG, "Cannot update " + stateToString(state) + " for destroyed " + mActivity);
             return;
         }
+        boolean isLoggable = Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG);
         Log.i(TAG, "updateUiTranslationState state: " + stateToString(state)
-                + (DEBUG ? (", views: " + views + ", spec: " + uiTranslationSpec) : ""));
+                + (isLoggable ? (", views: " + views + ", spec: " + uiTranslationSpec) : ""));
         synchronized (mLock) {
             mCurrentState = state;
             if (views != null) {
@@ -237,7 +238,7 @@
             }
             pw.print(outerPrefix); pw.print("padded views: "); pw.println(mViewsToPadContent);
         }
-        if (DEBUG) {
+        if (Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG)) {
             dumpViewByTraversal(outerPrefix, pw);
         }
     }
@@ -345,6 +346,7 @@
      */
     private void onVirtualViewTranslationCompleted(
             SparseArray<LongSparseArray<ViewTranslationResponse>> translatedResult) {
+        boolean isLoggable = Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG);
         if (mActivity.isDestroyed()) {
             Log.v(TAG, "onTranslationCompleted:" + mActivity + "is destroyed.");
             return;
@@ -369,7 +371,7 @@
                 }
                 final LongSparseArray<ViewTranslationResponse> virtualChildResponse =
                         translatedResult.valueAt(i);
-                if (DEBUG) {
+                if (isLoggable) {
                     Log.v(TAG, "onVirtualViewTranslationCompleted: received response for "
                             + "AutofillId " + autofillId);
                 }
@@ -379,7 +381,7 @@
                 }
                 mActivity.runOnUiThread(() -> {
                     if (view.getViewTranslationCallback() == null) {
-                        if (DEBUG) {
+                        if (isLoggable) {
                             Log.d(TAG, view + " doesn't support showing translation because of "
                                     + "null ViewTranslationCallback.");
                         }
@@ -397,12 +399,13 @@
      * The method is used to handle the translation result for non-vertual views.
      */
     private void onTranslationCompleted(SparseArray<ViewTranslationResponse> translatedResult) {
+        boolean isLoggable = Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG);
         if (mActivity.isDestroyed()) {
             Log.v(TAG, "onTranslationCompleted:" + mActivity + "is destroyed.");
             return;
         }
         final int resultCount = translatedResult.size();
-        if (DEBUG) {
+        if (isLoggable) {
             Log.v(TAG, "onTranslationCompleted: receive " + resultCount + " responses.");
         }
         synchronized (mLock) {
@@ -413,7 +416,7 @@
             }
             for (int i = 0; i < resultCount; i++) {
                 final ViewTranslationResponse response = translatedResult.valueAt(i);
-                if (DEBUG) {
+                if (isLoggable) {
                     Log.v(TAG, "onTranslationCompleted: "
                             + sanitizedViewTranslationResponse(response));
                 }
@@ -443,7 +446,7 @@
                                     (TextViewTranslationCallback) callback;
                             if (textViewCallback.isShowingTranslation()
                                     || textViewCallback.isAnimationRunning()) {
-                                if (DEBUG) {
+                                if (isLoggable) {
                                     Log.d(TAG, "Duplicate ViewTranslationResponse for " + autofillId
                                             + ". Ignoring.");
                                 }
@@ -458,7 +461,7 @@
                             callback = new TextViewTranslationCallback();
                             view.setViewTranslationCallback(callback);
                         } else {
-                            if (DEBUG) {
+                            if (isLoggable) {
                                 Log.d(TAG, view + " doesn't support showing translation because of "
                                         + "null ViewTranslationCallback.");
                             }
@@ -506,7 +509,7 @@
         final TranslationRequest request = new TranslationRequest.Builder()
                 .setViewTranslationRequests(requests)
                 .build();
-        if (DEBUG) {
+        if (Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG)) {
             StringBuilder msg = new StringBuilder("sendTranslationRequest:{requests=[");
             for (ViewTranslationRequest viewRequest: requests) {
                 msg.append("{request=")
@@ -636,6 +639,7 @@
 
     private void runForEachView(BiConsumer<View, ViewTranslationCallback> action) {
         synchronized (mLock) {
+            boolean isLoggable = Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG);
             final ArrayMap<AutofillId, WeakReference<View>> views = new ArrayMap<>(mViews);
             if (views.size() == 0) {
                 Log.w(TAG, "No views can be excuted for runForEachView.");
@@ -644,12 +648,12 @@
                 final int viewCounts = views.size();
                 for (int i = 0; i < viewCounts; i++) {
                     final View view = views.valueAt(i).get();
-                    if (DEBUG) {
+                    if (isLoggable) {
                         Log.d(TAG, "runForEachView for autofillId = " + (view != null
                                 ? view.getAutofillId() : " null"));
                     }
                     if (view == null || view.getViewTranslationCallback() == null) {
-                        if (DEBUG) {
+                        if (isLoggable) {
                             Log.d(TAG, "View was gone or ViewTranslationCallback for autofillId "
                                     + "= " + views.keyAt(i));
                         }
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index dd72689..410b441 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -48,12 +48,22 @@
 
     struct {
         jclass clazz;
+
         jmethodID init;
+
+        jfieldID vsyncId;
+        jfieldID expectedPresentationTime;
+        jfieldID deadline;
     } frameTimelineClassInfo;
 
     struct {
         jclass clazz;
+
         jmethodID init;
+
+        jfieldID frameInterval;
+        jfieldID preferredFrameTimelineIndex;
+        jfieldID frameTimelines;
     } vsyncEventDataClassInfo;
 
 } gDisplayEventReceiverClassInfo;
@@ -61,7 +71,7 @@
 
 class NativeDisplayEventReceiver : public DisplayEventDispatcher {
 public:
-    NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak,
+    NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak, jobject vsyncEventDataWeak,
                                const sp<MessageQueue>& messageQueue, jint vsyncSource,
                                jint eventRegistration, jlong layerHandle);
 
@@ -72,6 +82,7 @@
 
 private:
     jobject mReceiverWeakGlobal;
+    jobject mVsyncEventDataWeakGlobal;
     sp<MessageQueue> mMessageQueue;
 
     void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
@@ -85,6 +96,7 @@
 };
 
 NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak,
+                                                       jobject vsyncEventDataWeak,
                                                        const sp<MessageQueue>& messageQueue,
                                                        jint vsyncSource, jint eventRegistration,
                                                        jlong layerHandle)
@@ -96,6 +108,7 @@
                                                           reinterpret_cast<IBinder*>(layerHandle))
                                                 : nullptr),
         mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
+        mVsyncEventDataWeakGlobal(env->NewGlobalRef(vsyncEventDataWeak)),
         mMessageQueue(messageQueue) {
     ALOGV("receiver %p ~ Initializing display event receiver.", this);
 }
@@ -154,12 +167,43 @@
     JNIEnv* env = AndroidRuntime::getJNIEnv();
 
     ScopedLocalRef<jobject> receiverObj(env, GetReferent(env, mReceiverWeakGlobal));
-    if (receiverObj.get()) {
+    ScopedLocalRef<jobject> vsyncEventDataObj(env, GetReferent(env, mVsyncEventDataWeakGlobal));
+    if (receiverObj.get() && vsyncEventDataObj.get()) {
         ALOGV("receiver %p ~ Invoking vsync handler.", this);
 
-        jobject javaVsyncEventData = createJavaVsyncEventData(env, vsyncEventData);
+        env->SetIntField(vsyncEventDataObj.get(),
+                         gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo
+                                 .preferredFrameTimelineIndex,
+                         vsyncEventData.preferredFrameTimelineIndex);
+        env->SetLongField(vsyncEventDataObj.get(),
+                          gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.frameInterval,
+                          vsyncEventData.frameInterval);
+
+        ScopedLocalRef<jobjectArray>
+                frameTimelinesObj(env,
+                                  reinterpret_cast<jobjectArray>(
+                                          env->GetObjectField(vsyncEventDataObj.get(),
+                                                              gDisplayEventReceiverClassInfo
+                                                                      .vsyncEventDataClassInfo
+                                                                      .frameTimelines)));
+        for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+            VsyncEventData::FrameTimeline& frameTimeline = vsyncEventData.frameTimelines[i];
+            ScopedLocalRef<jobject>
+                    frameTimelineObj(env, env->GetObjectArrayElement(frameTimelinesObj.get(), i));
+            env->SetLongField(frameTimelineObj.get(),
+                              gDisplayEventReceiverClassInfo.frameTimelineClassInfo.vsyncId,
+                              frameTimeline.vsyncId);
+            env->SetLongField(frameTimelineObj.get(),
+                              gDisplayEventReceiverClassInfo.frameTimelineClassInfo
+                                      .expectedPresentationTime,
+                              frameTimeline.expectedPresentationTime);
+            env->SetLongField(frameTimelineObj.get(),
+                              gDisplayEventReceiverClassInfo.frameTimelineClassInfo.deadline,
+                              frameTimeline.deadlineTimestamp);
+        }
+
         env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync,
-                            timestamp, displayId.value, count, javaVsyncEventData);
+                            timestamp, displayId.value, count);
         ALOGV("receiver %p ~ Returned from vsync handler.", this);
     }
 
@@ -227,8 +271,9 @@
     mMessageQueue->raiseAndClearException(env, "dispatchModeChanged");
 }
 
-static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj,
-                        jint vsyncSource, jint eventRegistration, jlong layerHandle) {
+static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject vsyncEventDataWeak,
+                        jobject messageQueueObj, jint vsyncSource, jint eventRegistration,
+                        jlong layerHandle) {
     sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
     if (messageQueue == NULL) {
         jniThrowRuntimeException(env, "MessageQueue is not initialized.");
@@ -236,8 +281,8 @@
     }
 
     sp<NativeDisplayEventReceiver> receiver =
-            new NativeDisplayEventReceiver(env, receiverWeak, messageQueue, vsyncSource,
-                                           eventRegistration, layerHandle);
+            new NativeDisplayEventReceiver(env, receiverWeak, vsyncEventDataWeak, messageQueue,
+                                           vsyncSource, eventRegistration, layerHandle);
     status_t status = receiver->initialize();
     if (status) {
         String8 message;
@@ -284,7 +329,9 @@
 
 static const JNINativeMethod gMethods[] = {
         /* name, signature, funcPtr */
-        {"nativeInit", "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;IIJ)J",
+        {"nativeInit",
+         "(Ljava/lang/ref/WeakReference;Ljava/lang/ref/WeakReference;Landroid/os/"
+         "MessageQueue;IIJ)J",
          (void*)nativeInit},
         {"nativeGetDisplayEventReceiverFinalizer", "()J",
          (void*)nativeGetDisplayEventReceiverFinalizer},
@@ -301,8 +348,7 @@
     gDisplayEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
 
     gDisplayEventReceiverClassInfo.dispatchVsync =
-            GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync",
-                             "(JJILandroid/view/DisplayEventReceiver$VsyncEventData;)V");
+            GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJI)V");
     gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
             gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
     gDisplayEventReceiverClassInfo.dispatchModeChanged =
@@ -328,6 +374,15 @@
     gDisplayEventReceiverClassInfo.frameTimelineClassInfo.init =
             GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz,
                              "<init>", "(JJJ)V");
+    gDisplayEventReceiverClassInfo.frameTimelineClassInfo.vsyncId =
+            GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz,
+                            "vsyncId", "J");
+    gDisplayEventReceiverClassInfo.frameTimelineClassInfo.expectedPresentationTime =
+            GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz,
+                            "expectedPresentationTime", "J");
+    gDisplayEventReceiverClassInfo.frameTimelineClassInfo.deadline =
+            GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz,
+                            "deadline", "J");
 
     jclass vsyncEventDataClazz =
             FindClassOrDie(env, "android/view/DisplayEventReceiver$VsyncEventData");
@@ -339,6 +394,17 @@
                              "([Landroid/view/"
                              "DisplayEventReceiver$VsyncEventData$FrameTimeline;IJ)V");
 
+    gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.preferredFrameTimelineIndex =
+            GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz,
+                            "preferredFrameTimelineIndex", "I");
+    gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.frameInterval =
+            GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz,
+                            "frameInterval", "J");
+    gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.frameTimelines =
+            GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz,
+                            "frameTimelines",
+                            "[Landroid/view/DisplayEventReceiver$VsyncEventData$FrameTimeline;");
+
     return res;
 }
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9f10ae60..5544701 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -534,8 +534,10 @@
     <!-- If this is true, long press on power button will be available from the non-interactive state -->
     <bool name="config_supportLongPressPowerWhenNonInteractive">false</bool>
 
-    <!-- If this is true, then keep dreaming when undocking. -->
-    <bool name="config_keepDreamingWhenUndocking">false</bool>
+    <!-- If this is true, then keep dreaming when unplugging.
+         This config was formerly known as config_keepDreamingWhenUndocking.
+         It has been updated to affect other plug types. -->
+    <bool name="config_keepDreamingWhenUnplugging">false</bool>
 
     <!-- The timeout (in ms) to wait before attempting to reconnect to the dream overlay service if
          it becomes disconnected -->
@@ -4304,8 +4306,12 @@
          This package must be trusted, as it has the permissions to control other applications
          on the device.
          Example: "com.android.wellbeing"
+
+         Note: This config is deprecated, please use config_systemWellbeing instead.
      -->
-    <string name="config_defaultWellbeingPackage" translatable="false"></string>
+    <string name="config_defaultWellbeingPackage" translatable="false">
+        @string/config_systemWellbeing
+    </string>
 
     <!-- The component name for the default system attention service.
          This service must be trusted, as it can be activated without explicit consent of the user.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a823d1f..cea6032 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1944,7 +1944,7 @@
   <java-symbol type="bool" name="config_allowTheaterModeWakeFromLidSwitch" />
   <java-symbol type="bool" name="config_allowTheaterModeWakeFromDock" />
   <java-symbol type="bool" name="config_allowTheaterModeWakeFromWindowLayout" />
-  <java-symbol type="bool" name="config_keepDreamingWhenUndocking" />
+  <java-symbol type="bool" name="config_keepDreamingWhenUnplugging" />
   <java-symbol type="integer" name="config_keyguardDrawnTimeout" />
   <java-symbol type="bool" name="config_goToSleepOnButtonPressTheaterMode" />
   <java-symbol type="bool" name="config_supportLongPressPowerWhenNonInteractive" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index e59b259..7068453 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1665,47 +1665,47 @@
 
         <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
         <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
-        <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+        <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
         <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
-        <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
-        <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
-        <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
-        <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+        <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+        <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
+        <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+        <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
-        <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</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_dark</item>
-        <item name="materialColorErrorContainer">@color/system_error_container_dark</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="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
-        <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</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>
-        <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
-        <item name="materialColorOnBackground">@color/system_on_background_dark</item>
+        <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+        <item name="materialColorOnBackground">@color/system_on_background_light</item>
         <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
-        <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
-        <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
-        <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
-        <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
-        <item name="materialColorSecondary">@color/system_secondary_dark</item>
-        <item name="materialColorOnError">@color/system_on_error_dark</item>
-        <item name="materialColorSurface">@color/system_surface_dark</item>
-        <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
-        <item name="materialColorTertiary">@color/system_tertiary_dark</item>
-        <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
-        <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
-        <item name="materialColorOutline">@color/system_outline_dark</item>
-        <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
-        <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>
+        <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+        <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+        <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+        <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+        <item name="materialColorSecondary">@color/system_secondary_light</item>
+        <item name="materialColorOnError">@color/system_on_error_light</item>
+        <item name="materialColorSurface">@color/system_surface_light</item>
+        <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+        <item name="materialColorTertiary">@color/system_tertiary_light</item>
+        <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+        <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+        <item name="materialColorOutline">@color/system_outline_light</item>
+        <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
+        <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>
     </style>
 
     <!-- DeviceDefault style for input methods, which is used by the
@@ -1758,47 +1758,47 @@
 
         <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
         <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
-        <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+        <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
         <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
-        <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
-        <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
-        <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
-        <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+        <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+        <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
+        <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+        <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
-        <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</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_dark</item>
-        <item name="materialColorErrorContainer">@color/system_error_container_dark</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="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
-        <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</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>
-        <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
-        <item name="materialColorOnBackground">@color/system_on_background_dark</item>
+        <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+        <item name="materialColorOnBackground">@color/system_on_background_light</item>
         <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
-        <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
-        <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
-        <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
-        <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
-        <item name="materialColorSecondary">@color/system_secondary_dark</item>
-        <item name="materialColorOnError">@color/system_on_error_dark</item>
-        <item name="materialColorSurface">@color/system_surface_dark</item>
-        <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
-        <item name="materialColorTertiary">@color/system_tertiary_dark</item>
-        <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
-        <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
-        <item name="materialColorOutline">@color/system_outline_dark</item>
-        <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
-        <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>
+        <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+        <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+        <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+        <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+        <item name="materialColorSecondary">@color/system_secondary_light</item>
+        <item name="materialColorOnError">@color/system_on_error_light</item>
+        <item name="materialColorSurface">@color/system_surface_light</item>
+        <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+        <item name="materialColorTertiary">@color/system_tertiary_light</item>
+        <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+        <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+        <item name="materialColorOutline">@color/system_outline_light</item>
+        <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
+        <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>
     </style>
 
     <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert">
@@ -4043,16 +4043,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>
@@ -4123,16 +4123,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>
@@ -4195,16 +4195,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>
@@ -4366,16 +4366,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>
@@ -4475,47 +4475,47 @@
 
         <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
         <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
-        <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+        <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
         <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
-        <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
-        <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
-        <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
-        <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+        <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+        <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
+        <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+        <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
-        <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</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_dark</item>
-        <item name="materialColorErrorContainer">@color/system_error_container_dark</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="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
-        <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</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>
-        <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
-        <item name="materialColorOnBackground">@color/system_on_background_dark</item>
+        <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+        <item name="materialColorOnBackground">@color/system_on_background_light</item>
         <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
-        <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
-        <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
-        <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
-        <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
-        <item name="materialColorSecondary">@color/system_secondary_dark</item>
-        <item name="materialColorOnError">@color/system_on_error_dark</item>
-        <item name="materialColorSurface">@color/system_surface_dark</item>
-        <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
-        <item name="materialColorTertiary">@color/system_tertiary_dark</item>
-        <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
-        <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
-        <item name="materialColorOutline">@color/system_outline_dark</item>
-        <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
-        <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>
+        <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+        <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+        <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+        <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+        <item name="materialColorSecondary">@color/system_secondary_light</item>
+        <item name="materialColorOnError">@color/system_on_error_light</item>
+        <item name="materialColorSurface">@color/system_surface_light</item>
+        <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+        <item name="materialColorTertiary">@color/system_tertiary_light</item>
+        <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+        <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+        <item name="materialColorOutline">@color/system_outline_light</item>
+        <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
+        <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>
     </style>
 
     <style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.Alert">
@@ -4570,47 +4570,47 @@
 
         <item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
         <item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
-        <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+        <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
         <item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
-        <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
-        <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
-        <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
-        <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+        <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+        <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
+        <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+        <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
         <item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
-        <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</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_dark</item>
-        <item name="materialColorErrorContainer">@color/system_error_container_dark</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="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
-        <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</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>
-        <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
-        <item name="materialColorOnBackground">@color/system_on_background_dark</item>
+        <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+        <item name="materialColorOnBackground">@color/system_on_background_light</item>
         <item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
-        <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
-        <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
-        <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
-        <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
-        <item name="materialColorSecondary">@color/system_secondary_dark</item>
-        <item name="materialColorOnError">@color/system_on_error_dark</item>
-        <item name="materialColorSurface">@color/system_surface_dark</item>
-        <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
-        <item name="materialColorTertiary">@color/system_tertiary_dark</item>
-        <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
-        <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
-        <item name="materialColorOutline">@color/system_outline_dark</item>
-        <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
-        <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>
+        <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+        <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+        <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+        <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+        <item name="materialColorSecondary">@color/system_secondary_light</item>
+        <item name="materialColorOnError">@color/system_on_error_light</item>
+        <item name="materialColorSurface">@color/system_surface_light</item>
+        <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+        <item name="materialColorTertiary">@color/system_tertiary_light</item>
+        <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+        <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+        <item name="materialColorOutline">@color/system_outline_light</item>
+        <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
+        <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>
     </style>
 
     <style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" />
@@ -4700,16 +4700,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>
diff --git a/core/res/res/xml/irq_device_map.xml b/core/res/res/xml/irq_device_map.xml
index 4fae8fb..8b3667e 100644
--- a/core/res/res/xml/irq_device_map.xml
+++ b/core/res/res/xml/irq_device_map.xml
@@ -18,14 +18,16 @@
 -->
 <irq-device-map>
   <!--  This file maps devices (chips) that can send interrupts to the main processor (and bring it
-        out of sleep) to logical subsystems in userspace code. Since each Android device has its own
-        uniquely designed chipset, this mapping is expected to be empty by default and should be
-        overridden by device-specific configs.
+        out of sleep) to logical subsystems in userspace code. Since each Android device can have
+        a differently designed chipset, this mapping is expected to be empty by default and should
+        be overridden by device-specific configs.
         This mapping helps the system to meaningfully attribute CPU wakeups to logical work that
-        happened on the device. The devices are referred to by their names as defined in the kernel.
-        Currently, defined subsystems are:
-        - Alarm: Use this to denote wakeup alarms requested by apps via the AlarmManager API.
-        - Wifi: Use this to denote network traffic that uses the wifi transport.
+        happened on the device and the app activity that caused it. The devices are referred to by
+        their names as defined in the kernel. Currently, defined subsystems are:
+            - Alarm: Use this to denote wakeup alarms requested by apps via the AlarmManager API.
+            - Wifi: Use this to denote network traffic that uses the wifi transport.
+            - Sound_trigger: Use this to denote sound phrase detection, like the ones supported by
+        SoundTriggerManager.
 
         The overlay should use tags <device> and <subsystem> to describe this mapping in the
         following way:
diff --git a/core/tests/coretests/src/android/app/OWNERS b/core/tests/coretests/src/android/app/OWNERS
index b3f3993..8d9461d 100644
--- a/core/tests/coretests/src/android/app/OWNERS
+++ b/core/tests/coretests/src/android/app/OWNERS
@@ -4,3 +4,6 @@
 per-file *Notification* = file:/packages/SystemUI/OWNERS
 per-file *Zen* = file:/packages/SystemUI/OWNERS
 per-file *StatusBar* = file:/packages/SystemUI/OWNERS
+
+# A11Y and related
+per-file *UiAutomation* = file:/services/accessibility/OWNERS
diff --git a/data/etc/preinstalled-packages-platform-overlays.xml b/data/etc/preinstalled-packages-platform-overlays.xml
index 2fd65dc..cea535e 100644
--- a/data/etc/preinstalled-packages-platform-overlays.xml
+++ b/data/etc/preinstalled-packages-platform-overlays.xml
@@ -58,6 +58,7 @@
     </install-in-user-type>
     <install-in-user-type package="com.android.role.notes.enabled">
         <install-in user-type="FULL" />
+        <install-in user-type="PROFILE" />
     </install-in-user-type>
     <install-in-user-type package="com.android.theme.color.amethyst">
         <install-in user-type="FULL" />
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 0eb4caa..7434cb0 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -175,12 +175,6 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "-1941440781": {
-      "message": "Creating Pending Move-to-back: %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/Task.java"
-    },
     "-1939861963": {
       "message": "Create root task displayId=%d winMode=%d",
       "level": "VERBOSE",
@@ -631,12 +625,6 @@
       "group": "WM_DEBUG_CONFIGURATION",
       "at": "com\/android\/server\/wm\/ActivityStarter.java"
     },
-    "-1484988952": {
-      "message": "Creating Pending Multiwindow Fullscreen Request: %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/ActivityClientController.java"
-    },
     "-1483435730": {
       "message": "InsetsSource setWin %s for type %s",
       "level": "DEBUG",
@@ -2605,12 +2593,6 @@
       "group": "WM_DEBUG_ANIM",
       "at": "com\/android\/server\/wm\/WindowState.java"
     },
-    "286170861": {
-      "message": "Creating Pending Transition for TaskFragment: %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/WindowOrganizerController.java"
-    },
     "288485303": {
       "message": "Attempted to set remove mode to a display that does not exist: %d",
       "level": "WARN",
@@ -3193,12 +3175,6 @@
       "group": "WM_DEBUG_APP_TRANSITIONS",
       "at": "com\/android\/server\/wm\/AppTransitionController.java"
     },
-    "800698875": {
-      "message": "SyncGroup %d: Started when there is other active SyncGroup",
-      "level": "WARN",
-      "group": "WM_DEBUG_SYNC_ENGINE",
-      "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
-    },
     "801521566": {
       "message": "Content Recording: Attempting to mirror %d from %d but no DisplayContent associated. Changing to mirror default display.",
       "level": "WARN",
@@ -3259,12 +3235,6 @@
       "group": "WM_DEBUG_STATES",
       "at": "com\/android\/server\/wm\/RootWindowContainer.java"
     },
-    "898260097": {
-      "message": "Creating Pending Pip-Enter: %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
-    },
     "898863925": {
       "message": "Attempted to add QS dialog window with unknown token %s.  Aborting.",
       "level": "WARN",
@@ -4009,12 +3979,6 @@
       "group": "WM_DEBUG_CONTENT_RECORDING",
       "at": "com\/android\/server\/wm\/ContentRecorder.java"
     },
-    "1667162379": {
-      "message": "Creating Pending Transition: %s",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
-      "at": "com\/android\/server\/wm\/WindowOrganizerController.java"
-    },
     "1670933628": {
       "message": " Setting allReady override",
       "level": "VERBOSE",
@@ -4075,6 +4039,12 @@
       "group": "WM_DEBUG_SYNC_ENGINE",
       "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
     },
+    "1735199721": {
+      "message": "Queueing transition: %s",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN",
+      "at": "com\/android\/server\/wm\/TransitionController.java"
+    },
     "1739298851": {
       "message": "removeWindowToken: Attempted to remove token: %s for non-exiting displayId=%d",
       "level": "WARN",
diff --git a/data/keyboards/GoogleTV-Remote.idc b/data/keyboards/GoogleTV-Remote.idc
new file mode 100644
index 0000000..14fb4e2
--- /dev/null
+++ b/data/keyboards/GoogleTV-Remote.idc
@@ -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.
+
+#
+# Input Device Configuration file for Google Reference Remote Control Unit (RCU).
+#
+#
+
+# Basic Parameters
+# Depending on the FLASH configurations, RCUs may have PID 0006 instead
+# of 0001.
+keyboard.layout = Vendor_0957_Product_0001
+keyboard.doNotWakeByDefault = 1
+audio.mic = 1
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
index 521a65c..bfbddbb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
@@ -22,6 +22,7 @@
 import static java.util.Objects.requireNonNull;
 
 import android.content.Context;
+import android.graphics.Rect;
 import android.os.IBinder;
 import android.util.ArrayMap;
 import android.view.SurfaceControl;
@@ -35,6 +36,9 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.util.TransitionUtil;
+
+import java.util.List;
 
 /**
  * Responsible for handling ActivityEmbedding related transitions.
@@ -86,12 +90,13 @@
             @NonNull SurfaceControl.Transaction finishTransaction,
             @NonNull Transitions.TransitionFinishCallback finishCallback) {
         boolean containsEmbeddingSplit = false;
-        for (TransitionInfo.Change change : info.getChanges()) {
+        boolean containsNonEmbeddedChange = false;
+        final List<TransitionInfo.Change> changes = info.getChanges();
+        for (int i = changes.size() - 1; i >= 0; i--) {
+            final TransitionInfo.Change change = changes.get(i);
             if (!change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
-                // Only animate the transition if all changes are in a Task with ActivityEmbedding.
-                return false;
-            }
-            if (!containsEmbeddingSplit && !change.hasFlags(FLAG_FILLS_TASK)) {
+                containsNonEmbeddedChange = true;
+            } else if (!change.hasFlags(FLAG_FILLS_TASK)) {
                 // Whether the Task contains any ActivityEmbedding split before or after the
                 // transition.
                 containsEmbeddingSplit = true;
@@ -103,6 +108,9 @@
             // such as the device is in a folded state.
             return false;
         }
+        if (containsNonEmbeddedChange && !handleNonEmbeddedChanges(changes)) {
+            return false;
+        }
 
         // Start ActivityEmbedding animation.
         mTransitionCallbacks.put(transition, finishCallback);
@@ -110,6 +118,37 @@
         return true;
     }
 
+    private boolean handleNonEmbeddedChanges(List<TransitionInfo.Change> changes) {
+        final Rect nonClosingEmbeddedArea = new Rect();
+        for (int i = changes.size() - 1; i >= 0; i--) {
+            final TransitionInfo.Change change = changes.get(i);
+            if (!TransitionUtil.isClosingType(change.getMode())) {
+                if (change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
+                    nonClosingEmbeddedArea.union(change.getEndAbsBounds());
+                    continue;
+                }
+                // Not able to handle non-embedded container if it is not closing.
+                return false;
+            }
+        }
+        for (int i = changes.size() - 1; i >= 0; i--) {
+            final TransitionInfo.Change change = changes.get(i);
+            if (!change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)
+                    && !nonClosingEmbeddedArea.contains(change.getEndAbsBounds())) {
+                // Unknown to animate containers outside the area of embedded activities.
+                return false;
+            }
+        }
+        // Drop the non-embedded closing change because it is occluded by embedded activities.
+        for (int i = changes.size() - 1; i >= 0; i--) {
+            final TransitionInfo.Change change = changes.get(i);
+            if (!change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
+                changes.remove(i);
+            }
+        }
+        return true;
+    }
+
     @Nullable
     @Override
     public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 8c98c77..1d7e649 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -300,7 +300,7 @@
          * @return true if handled by the handler, false otherwise.
          */
         public boolean handlePipTransaction(SurfaceControl leash, SurfaceControl.Transaction tx,
-                Rect destinationBounds) {
+                Rect destinationBounds, float alpha) {
             return false;
         }
     }
@@ -401,9 +401,10 @@
         }
 
         boolean handlePipTransaction(SurfaceControl leash, SurfaceControl.Transaction tx,
-                Rect destinationBounds) {
+                Rect destinationBounds, float alpha) {
             if (mPipTransactionHandler != null) {
-                return mPipTransactionHandler.handlePipTransaction(leash, tx, destinationBounds);
+                return mPipTransactionHandler.handlePipTransaction(
+                        leash, tx, destinationBounds, alpha);
             }
             return false;
         }
@@ -548,7 +549,9 @@
                     getSurfaceTransactionHelper().alpha(tx, leash, alpha)
                             .round(tx, leash, shouldApplyCornerRadius())
                             .shadow(tx, leash, shouldApplyShadowRadius());
-                    tx.apply();
+                    if (!handlePipTransaction(leash, tx, destinationBounds, alpha)) {
+                        tx.apply();
+                    }
                 }
 
                 @Override
@@ -663,7 +666,7 @@
                                     .shadow(tx, leash, shouldApplyShadowRadius());
                         }
                     }
-                    if (!handlePipTransaction(leash, tx, bounds)) {
+                    if (!handlePipTransaction(leash, tx, bounds, /* alpha= */ 1f)) {
                         tx.apply();
                     }
                 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
index 0006244..0775f52 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
@@ -45,6 +45,13 @@
     String MENU_WINDOW_TITLE = "PipMenuView";
 
     /**
+     * Used with
+     * {@link PipMenuController#movePipMenu(SurfaceControl, SurfaceControl.Transaction, Rect,
+     * float)} to indicate that we don't want to affect the alpha value of the menu surfaces.
+     */
+    float ALPHA_NO_CHANGE = -1f;
+
+    /**
      * Called when
      * {@link PipTaskOrganizer#onTaskAppeared(RunningTaskInfo, SurfaceControl)}
      * is called.
@@ -85,8 +92,8 @@
      * need to synchronize the movements on the same frame as PiP.
      */
     default void movePipMenu(@Nullable SurfaceControl pipLeash,
-            @Nullable SurfaceControl.Transaction t,
-            Rect destinationBounds) {}
+            @Nullable SurfaceControl.Transaction t, Rect destinationBounds, float alpha) {
+    }
 
     /**
      * Update the PiP menu with the given bounds for re-layout purposes.
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 5670fe6..23706a5 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
@@ -249,7 +249,7 @@
         }, null);
     }
 
-    private boolean shouldSyncPipTransactionWithMenu() {
+    protected boolean shouldSyncPipTransactionWithMenu() {
         return mPipMenuController.isMenuVisible();
     }
 
@@ -277,9 +277,9 @@
             new PipAnimationController.PipTransactionHandler() {
                 @Override
                 public boolean handlePipTransaction(SurfaceControl leash,
-                        SurfaceControl.Transaction tx, Rect destinationBounds) {
+                        SurfaceControl.Transaction tx, Rect destinationBounds, float alpha) {
                     if (shouldSyncPipTransactionWithMenu()) {
-                        mPipMenuController.movePipMenu(leash, tx, destinationBounds);
+                        mPipMenuController.movePipMenu(leash, tx, destinationBounds, alpha);
                         return true;
                     }
                     return false;
@@ -381,6 +381,10 @@
         return mPipTransitionController;
     }
 
+    PipAnimationController.PipTransactionHandler getPipTransactionHandler() {
+        return mPipTransactionHandler;
+    }
+
     public Rect getCurrentOrAnimatingBounds() {
         PipAnimationController.PipTransitionAnimator animator =
                 mPipAnimationController.getCurrentAnimator();
@@ -1385,7 +1389,7 @@
                 .scale(tx, mLeash, startBounds, toBounds, degrees)
                 .round(tx, mLeash, startBounds, toBounds);
         if (shouldSyncPipTransactionWithMenu()) {
-            mPipMenuController.movePipMenu(mLeash, tx, toBounds);
+            mPipMenuController.movePipMenu(mLeash, tx, toBounds, PipMenuController.ALPHA_NO_CHANGE);
         } else {
             tx.apply();
         }
@@ -1551,7 +1555,8 @@
         if (!isInPip()) {
             return;
         }
-        mPipMenuController.movePipMenu(null, null, destinationBounds);
+        mPipMenuController.movePipMenu(null, null, destinationBounds,
+                PipMenuController.ALPHA_NO_CHANGE);
         mPipMenuController.updateMenuBounds(destinationBounds);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index b743140..c5e9229 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -823,6 +823,7 @@
             throw new RuntimeException("Unrecognized animation type: " + enterAnimationType);
         }
         animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
+                .setPipTransactionHandler(mPipOrganizer.getPipTransactionHandler())
                 .setPipAnimationCallback(mPipAnimationCallback)
                 .setDuration(mEnterExitAnimationDuration);
         if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) {
@@ -949,7 +950,8 @@
     }
 
     private void finishResizeForMenu(Rect destinationBounds) {
-        mPipMenuController.movePipMenu(null, null, destinationBounds);
+        mPipMenuController.movePipMenu(null, null, destinationBounds,
+                PipMenuController.ALPHA_NO_CHANGE);
         mPipMenuController.updateMenuBounds(destinationBounds);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 94e593b..e7a1395 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -298,7 +298,8 @@
         }
 
         // Sync the menu bounds before showing it in case it is out of sync.
-        movePipMenu(null /* pipLeash */, null /* transaction */, stackBounds);
+        movePipMenu(null /* pipLeash */, null /* transaction */, stackBounds,
+                PipMenuController.ALPHA_NO_CHANGE);
         updateMenuBounds(stackBounds);
 
         mPipMenuView.showMenu(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay,
@@ -311,7 +312,7 @@
     @Override
     public void movePipMenu(@Nullable SurfaceControl pipLeash,
             @Nullable SurfaceControl.Transaction t,
-            Rect destinationBounds) {
+            Rect destinationBounds, float alpha) {
         if (destinationBounds.isEmpty()) {
             return;
         }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index b18e21c..b2a189b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -30,6 +30,7 @@
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.ViewRootImpl;
+import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 import android.window.SurfaceSyncGroup;
 
@@ -202,8 +203,10 @@
     }
 
     private void addPipMenuViewToSystemWindows(View v, String title) {
-        mSystemWindows.addView(v, getPipMenuLayoutParams(mContext, title, 0 /* width */,
-                0 /* height */), 0 /* displayId */, SHELL_ROOT_LAYER_PIP);
+        final WindowManager.LayoutParams layoutParams =
+                getPipMenuLayoutParams(mContext, title, 0 /* width */, 0 /* height */);
+        layoutParams.alpha = 0f;
+        mSystemWindows.addView(v, layoutParams, 0 /* displayId */, SHELL_ROOT_LAYER_PIP);
     }
 
     void onPipTransitionFinished(boolean enterTransition) {
@@ -309,9 +312,9 @@
 
     @Override
     public void movePipMenu(SurfaceControl pipLeash, SurfaceControl.Transaction pipTx,
-            Rect pipBounds) {
+            Rect pipBounds, float alpha) {
         ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
-                "%s: movePipMenu: %s", TAG, pipBounds.toShortString());
+                "%s: movePipMenu: %s, alpha %s", TAG, pipBounds.toShortString(), alpha);
 
         if (pipBounds.isEmpty()) {
             if (pipTx == null) {
@@ -333,6 +336,11 @@
         pipTx.setPosition(frontSurface, menuDestBounds.left, menuDestBounds.top);
         pipTx.setPosition(backSurface, menuDestBounds.left, menuDestBounds.top);
 
+        if (alpha != ALPHA_NO_CHANGE) {
+            pipTx.setAlpha(frontSurface, alpha);
+            pipTx.setAlpha(backSurface, alpha);
+        }
+
         // Synchronize drawing the content in the front and back surfaces together with the pip
         // transaction and the position change for the front and back surfaces
         final SurfaceSyncGroup syncGroup = new SurfaceSyncGroup("TvPip");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
index 0940490..4819f66 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
@@ -98,4 +98,11 @@
     protected boolean shouldAlwaysFadeIn() {
         return true;
     }
+
+    @Override
+    protected boolean shouldSyncPipTransactionWithMenu() {
+        // We always have a menu visible and want to sync the pip transaction with the menu, even
+        // when the menu alpha is 0 (e.g. when a fade-in animation starts).
+        return true;
+    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 2cd16be..498f95c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -541,6 +541,34 @@
                 instanceId);
     }
 
+    void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1,
+            int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
+            float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+        if (options1 == null) options1 = new Bundle();
+        final ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
+        final String packageName1 = shortcutInfo.getPackage();
+        // NOTE: This doesn't correctly pull out packageName2 if taskId is referring to a task in
+        //       recents that hasn't launched and is not being organized
+        final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
+        if (samePackage(packageName1, packageName2)) {
+            if (supportMultiInstancesSplit(packageName1)) {
+                activityOptions.setApplyMultipleTaskFlagForShortcut(true);
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
+            } else {
+                if (mRecentTasksOptional.isPresent()) {
+                    mRecentTasksOptional.get().removeSplitPair(taskId);
+                }
+                taskId = INVALID_TASK_ID;
+                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+                        "Cancel entering split as not supporting multi-instances");
+                Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
+                        Toast.LENGTH_SHORT).show();
+            }
+        }
+        mStageCoordinator.startShortcutAndTask(shortcutInfo, options1, taskId, options2,
+                splitPosition, splitRatio, remoteTransition, instanceId);
+    }
+
     /**
      * See {@link #startIntent(PendingIntent, Intent, int, Bundle)}
      * @param instanceId to be used by {@link SplitscreenEventLogger}
@@ -580,6 +608,8 @@
             float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
         Intent fillInIntent = null;
         final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
+        // NOTE: This doesn't correctly pull out packageName2 if taskId is referring to a task in
+        //       recents that hasn't launched and is not being organized
         final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
         if (samePackage(packageName1, packageName2)) {
             if (supportMultiInstancesSplit(packageName1)) {
@@ -587,6 +617,10 @@
                 fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
             } else {
+                if (mRecentTasksOptional.isPresent()) {
+                    mRecentTasksOptional.get().removeSplitPair(taskId);
+                }
+                taskId = INVALID_TASK_ID;
                 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
                         "Cancel entering split as not supporting multi-instances");
                 Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
@@ -1075,9 +1109,8 @@
                 float splitRatio, @Nullable RemoteTransition remoteTransition,
                 InstanceId instanceId) {
             executeRemoteCallWithTaskPermission(mController, "startShortcutAndTask",
-                    (controller) -> controller.mStageCoordinator.startShortcutAndTask(shortcutInfo,
-                            options1, taskId, options2, splitPosition, splitRatio, remoteTransition,
-                            instanceId));
+                    (controller) -> controller.startShortcutAndTask(shortcutInfo, options1, taskId,
+                            options2, splitPosition, splitRatio, remoteTransition, instanceId));
         }
 
         @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 8b890bb..2c08cd4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -289,6 +289,12 @@
         return null;
     }
 
+    void startFullscreenTransition(WindowContainerTransaction wct,
+            @Nullable RemoteTransition handler) {
+        mTransitions.startTransition(TRANSIT_OPEN, wct,
+                new OneShotRemoteHandler(mTransitions.getMainExecutor(), handler));
+    }
+
 
     /** Starts a transition to enter split with a remote transition animator. */
     IBinder startEnterTransition(
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index ce5a2af6..e4f2724 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -612,6 +612,19 @@
             @Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
             @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
+        if (taskId2 == INVALID_TASK_ID) {
+            if (mMainStage.containsTask(taskId1) || mSideStage.containsTask(taskId1)) {
+                prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
+            }
+            if (mRecentTasks.isPresent()) {
+                mRecentTasks.get().removeSplitPair(taskId1);
+            }
+            options1 = options1 != null ? options1 : new Bundle();
+            wct.startTask(taskId1, options1);
+            mSplitTransitions.startFullscreenTransition(wct, remoteTransition);
+            return;
+        }
+
         prepareEvictChildTasksIfSplitActive(wct);
         setSideStagePosition(splitPosition, wct);
         options1 = options1 != null ? options1 : new Bundle();
@@ -627,6 +640,13 @@
             @SplitPosition int splitPosition, float splitRatio,
             @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
+        if (taskId == INVALID_TASK_ID) {
+            options1 = options1 != null ? options1 : new Bundle();
+            wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
+            mSplitTransitions.startFullscreenTransition(wct, remoteTransition);
+            return;
+        }
+
         prepareEvictChildTasksIfSplitActive(wct);
         setSideStagePosition(splitPosition, wct);
         options1 = options1 != null ? options1 : new Bundle();
@@ -641,6 +661,13 @@
             int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
             float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
+        if (taskId == INVALID_TASK_ID) {
+            options1 = options1 != null ? options1 : new Bundle();
+            wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
+            mSplitTransitions.startFullscreenTransition(wct, remoteTransition);
+            return;
+        }
+
         prepareEvictChildTasksIfSplitActive(wct);
         setSideStagePosition(splitPosition, wct);
         options1 = options1 != null ? options1 : new Bundle();
@@ -689,6 +716,17 @@
             @SplitPosition int splitPosition, float splitRatio,
             @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
         final WindowContainerTransaction wct = new WindowContainerTransaction();
+        if (pendingIntent2 == null) {
+            options1 = options1 != null ? options1 : new Bundle();
+            if (shortcutInfo1 != null) {
+                wct.startShortcut(mContext.getPackageName(), shortcutInfo1, options1);
+            } else {
+                wct.sendPendingIntent(pendingIntent1, fillInIntent1, options1);
+            }
+            mSplitTransitions.startFullscreenTransition(wct, remoteTransition);
+            return;
+        }
+
         if (!mMainStage.isActive()) {
             // Build a request WCT that will launch both apps such that task 0 is on the main stage
             // while task 1 is on the side stage.
@@ -2376,9 +2414,14 @@
                 }
             }
             if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
-                // TODO(shell-transitions): Implement a fallback behavior for now.
-                throw new IllegalStateException("Somehow removed the last task in a stage"
-                        + " outside of a proper transition");
+                Log.e(TAG, "Somehow removed the last task in a stage outside of a proper "
+                        + "transition.");
+                final WindowContainerTransaction wct = new WindowContainerTransaction();
+                final int dismissTop = mMainStage.getChildCount() == 0
+                        ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
+                prepareExitSplitScreen(dismissTop, wct);
+                mSplitTransitions.startDismissTransition(wct, this, dismissTop,
+                        EXIT_REASON_UNKNOWN);
                 // This can happen in some pathological cases. For example:
                 // 1. main has 2 tasks [Task A (Single-task), Task B], side has one task [Task C]
                 // 2. Task B closes itself and starts Task A in LAUNCH_ADJACENT at the same time
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 3dd10a0..6e9ecda 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
@@ -327,6 +327,7 @@
 
         @ColorInt int backgroundColorForTransition = 0;
         final int wallpaperTransit = getWallpaperTransitType(info);
+        boolean isDisplayRotationAnimationStarted = false;
         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
             final TransitionInfo.Change change = info.getChanges().get(i);
             if (change.hasAllFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY
@@ -350,6 +351,7 @@
                     if (!(isSeamlessDisplayChange || anim == ROTATION_ANIMATION_JUMPCUT)) {
                         startRotationAnimation(startTransaction, change, info, anim, animations,
                                 onAnimFinish);
+                        isDisplayRotationAnimationStarted = true;
                         continue;
                     }
                 } else {
@@ -405,6 +407,14 @@
                 }
             }
 
+            // Hide the invisible surface directly without animating it if there is a display
+            // rotation animation playing.
+            if (isDisplayRotationAnimationStarted && TransitionUtil.isClosingType(
+                    change.getMode())) {
+                startTransaction.hide(change.getLeash());
+                continue;
+            }
+
             // The back gesture has animated this change before transition happen, so here we don't
             // play the animation again.
             if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
index cbbb291..b8f615a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
@@ -16,6 +16,7 @@
 
 package com.android.wm.shell.activityembedding;
 
+import static android.view.WindowManager.TRANSIT_CLOSE;
 import static android.view.WindowManager.TRANSIT_OPEN;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
@@ -82,10 +83,13 @@
 
     @Test
     public void testStartAnimation_containsNonActivityEmbeddingChange() {
+        final TransitionInfo.Change nonEmbeddedOpen = createChange(0 /* flags */);
+        final TransitionInfo.Change embeddedOpen = createEmbeddedChange(
+                EMBEDDED_LEFT_BOUNDS, EMBEDDED_LEFT_BOUNDS, TASK_BOUNDS);
+        nonEmbeddedOpen.setMode(TRANSIT_OPEN);
         final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
-                .addChange(createEmbeddedChange(
-                        EMBEDDED_LEFT_BOUNDS, EMBEDDED_LEFT_BOUNDS, TASK_BOUNDS))
-                .addChange(createChange(0 /* flags */))
+                .addChange(embeddedOpen)
+                .addChange(nonEmbeddedOpen)
                 .build();
 
         // No-op because it contains non-embedded change.
@@ -95,6 +99,22 @@
         verifyNoMoreInteractions(mStartTransaction);
         verifyNoMoreInteractions(mFinishTransaction);
         verifyNoMoreInteractions(mFinishCallback);
+
+        final TransitionInfo.Change nonEmbeddedClose = createChange(0 /* flags */);
+        nonEmbeddedClose.setMode(TRANSIT_CLOSE);
+        nonEmbeddedClose.setEndAbsBounds(TASK_BOUNDS);
+        final TransitionInfo.Change embeddedOpen2 = createEmbeddedChange(
+                EMBEDDED_RIGHT_BOUNDS, EMBEDDED_RIGHT_BOUNDS, TASK_BOUNDS);
+        final TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
+                .addChange(embeddedOpen)
+                .addChange(embeddedOpen2)
+                .addChange(nonEmbeddedClose)
+                .build();
+        // Ok to animate because nonEmbeddedClose is occluded by embeddedOpen and embeddedOpen2.
+        assertTrue(mController.startAnimation(mTransition, info2, mStartTransaction,
+                mFinishTransaction, mFinishCallback));
+        // The non-embedded change is dropped to avoid affecting embedded animation.
+        assertFalse(info2.getChanges().contains(nonEmbeddedClose));
     }
 
     @Test
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 718d4a1..96bfc10 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -42,6 +42,36 @@
 namespace uirenderer {
 namespace renderthread {
 
+static std::array<std::string_view, 18> sEnableExtensions{
+        VK_KHR_BIND_MEMORY_2_EXTENSION_NAME,
+        VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
+        VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME,
+        VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,
+        VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME,
+        VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
+        VK_KHR_MAINTENANCE1_EXTENSION_NAME,
+        VK_KHR_MAINTENANCE2_EXTENSION_NAME,
+        VK_KHR_MAINTENANCE3_EXTENSION_NAME,
+        VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
+        VK_KHR_SURFACE_EXTENSION_NAME,
+        VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+        VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME,
+        VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME,
+        VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
+        VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME,
+        VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,
+        VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
+};
+
+static bool shouldEnableExtension(const std::string_view& extension) {
+    for (const auto& it : sEnableExtensions) {
+        if (it == extension) {
+            return true;
+        }
+    }
+    return false;
+}
+
 static void free_features_extensions_structs(const VkPhysicalDeviceFeatures2& features) {
     // All Vulkan structs that could be part of the features chain will start with the
     // structure type followed by the pNext pointer. We cast to the CommonVulkanHeader
@@ -139,6 +169,11 @@
         bool hasKHRSurfaceExtension = false;
         bool hasKHRAndroidSurfaceExtension = false;
         for (const VkExtensionProperties& extension : mInstanceExtensionsOwner) {
+            if (!shouldEnableExtension(extension.extensionName)) {
+                ALOGV("Not enabling instance extension %s", extension.extensionName);
+                continue;
+            }
+            ALOGV("Enabling instance extension %s", extension.extensionName);
             mInstanceExtensions.push_back(extension.extensionName);
             if (!strcmp(extension.extensionName, VK_KHR_SURFACE_EXTENSION_NAME)) {
                 hasKHRSurfaceExtension = true;
@@ -219,6 +254,11 @@
         LOG_ALWAYS_FATAL_IF(VK_SUCCESS != err);
         bool hasKHRSwapchainExtension = false;
         for (const VkExtensionProperties& extension : mDeviceExtensionsOwner) {
+            if (!shouldEnableExtension(extension.extensionName)) {
+                ALOGV("Not enabling device extension %s", extension.extensionName);
+                continue;
+            }
+            ALOGV("Enabling device extension %s", extension.extensionName);
             mDeviceExtensions.push_back(extension.extensionName);
             if (!strcmp(extension.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
                 hasKHRSwapchainExtension = true;
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 116237f..609c7a4 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -196,6 +196,15 @@
         dstSize = (size_t) env->GetDirectBufferCapacity(byteBuf);
     }
 
+    // unlikely, but GetByteArrayElements() can fail
+    if (dst == nullptr) {
+        ALOGE("no buffer into which to read the data");
+        if (byteArray != NULL) {
+            env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
+        }
+        return -ENOMEM;
+    }
+
     if (dstSize < offset) {
         if (byteArray != NULL) {
             env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
@@ -204,8 +213,10 @@
         return -ERANGE;
     }
 
+    // passes in the backing memory to use, so it doesn't fail
     sp<ABuffer> buffer = new ABuffer((char *)dst + offset, dstSize - offset);
 
+    buffer->setRange(0, 0);  // mark it empty
     status_t err = mImpl->readSampleData(buffer);
 
     if (byteArray != NULL) {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 24f92c0..7581b5c 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -19,9 +19,9 @@
 import android.content.Intent
 import android.credentials.ui.BaseDialogResult
 import android.credentials.ui.RequestInfo
+import android.net.Uri
 import android.os.Bundle
 import android.os.ResultReceiver
-import android.provider.Settings
 import android.util.Log
 import androidx.activity.ComponentActivity
 import androidx.activity.compose.rememberLauncherForActivityResult
@@ -192,7 +192,9 @@
             this@CredentialSelectorActivity.finish()
         } else if (dialogState == DialogState.CANCELED_FOR_SETTINGS) {
             Log.d(Constants.LOG_TAG, "Received signal to finish the activity and launch settings.")
-            this@CredentialSelectorActivity.startActivity(Intent(Settings.ACTION_SYNC_SETTINGS))
+            val settingsIntent = Intent(ACTION_CREDENTIAL_PROVIDER)
+            settingsIntent.data = Uri.parse("package:" + this.getPackageName())
+            this@CredentialSelectorActivity.startActivity(settingsIntent)
             this@CredentialSelectorActivity.finish()
         }
     }
@@ -222,4 +224,8 @@
             dismissOnTimeout = true,
         )
     }
+
+    companion object {
+        const val ACTION_CREDENTIAL_PROVIDER = "android.settings.CREDENTIAL_PROVIDER"
+    }
 }
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index e8d3b1f..108f494 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -198,7 +198,8 @@
                     it.type,
                     it.credentialRetrievalData,
                     it.credentialRetrievalData,
-                    it.isSystemProviderRequired
+                    it.isSystemProviderRequired,
+                    it.allowedProviders,
                 )
                 if (credentialOptionJetpack is GetPublicKeyCredentialOption) {
                     credentialOptionJetpack.preferImmediatelyAvailableCredentials
@@ -462,7 +463,8 @@
                 createCredentialRequest.type,
                 createCredentialRequest.credentialData,
                 createCredentialRequest.candidateQueryData,
-                createCredentialRequest.isSystemProviderRequired
+                createCredentialRequest.isSystemProviderRequired,
+                createCredentialRequest.origin,
             )
             val appPreferredDefaultProviderId: String? =
                 if (!requestInfo.hasPermissionToOverrideDefault()) null
diff --git a/packages/DynamicSystemInstallationService/AndroidManifest.xml b/packages/DynamicSystemInstallationService/AndroidManifest.xml
index c2aaeac..776bf2b 100644
--- a/packages/DynamicSystemInstallationService/AndroidManifest.xml
+++ b/packages/DynamicSystemInstallationService/AndroidManifest.xml
@@ -36,6 +36,10 @@
                 <data android:scheme="http" />
                 <data android:scheme="https" />
             </intent-filter>
+            <intent-filter>
+                <action android:name="android.os.image.action.START_INSTALL" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
         </activity>
 
         <receiver
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 2c4b478..b265a42 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -19,6 +19,7 @@
 import static android.os.AsyncTask.Status.FINISHED;
 import static android.os.AsyncTask.Status.PENDING;
 import static android.os.AsyncTask.Status.RUNNING;
+import static android.os.image.DynamicSystemClient.ACTION_HIDE_NOTIFICATION;
 import static android.os.image.DynamicSystemClient.ACTION_NOTIFY_IF_IN_USE;
 import static android.os.image.DynamicSystemClient.ACTION_START_INSTALL;
 import static android.os.image.DynamicSystemClient.CAUSE_ERROR_EXCEPTION;
@@ -27,6 +28,8 @@
 import static android.os.image.DynamicSystemClient.CAUSE_INSTALL_CANCELLED;
 import static android.os.image.DynamicSystemClient.CAUSE_INSTALL_COMPLETED;
 import static android.os.image.DynamicSystemClient.CAUSE_NOT_SPECIFIED;
+import static android.os.image.DynamicSystemClient.KEY_ENABLE_WHEN_COMPLETED;
+import static android.os.image.DynamicSystemClient.KEY_ONE_SHOT;
 import static android.os.image.DynamicSystemClient.STATUS_IN_PROGRESS;
 import static android.os.image.DynamicSystemClient.STATUS_IN_USE;
 import static android.os.image.DynamicSystemClient.STATUS_NOT_STARTED;
@@ -77,8 +80,6 @@
 
     private static final String TAG = "DynamicSystemInstallationService";
 
-    // TODO (b/131866826): This is currently for test only. Will move this to System API.
-    static final String KEY_ENABLE_WHEN_COMPLETED = "KEY_ENABLE_WHEN_COMPLETED";
     static final String KEY_DSU_SLOT = "KEY_DSU_SLOT";
     static final String DEFAULT_DSU_SLOT = "dsu";
     static final String KEY_PUBKEY = "KEY_PUBKEY";
@@ -172,6 +173,8 @@
 
     // This is for testing only now
     private boolean mEnableWhenCompleted;
+    private boolean mOneShot;
+    private boolean mHideNotification;
 
     private InstallationAsyncTask.Progress mInstallTaskProgress;
     private InstallationAsyncTask mInstallTask;
@@ -229,6 +232,8 @@
             executeRebootToNormalCommand();
         } else if (ACTION_NOTIFY_IF_IN_USE.equals(action)) {
             executeNotifyIfInUseCommand();
+        } else if (ACTION_HIDE_NOTIFICATION.equals(action)) {
+            executeHideNotificationCommand();
         }
 
         return Service.START_NOT_STICKY;
@@ -318,6 +323,7 @@
         long systemSize = intent.getLongExtra(DynamicSystemClient.KEY_SYSTEM_SIZE, 0);
         long userdataSize = intent.getLongExtra(DynamicSystemClient.KEY_USERDATA_SIZE, 0);
         mEnableWhenCompleted = intent.getBooleanExtra(KEY_ENABLE_WHEN_COMPLETED, false);
+        mOneShot = intent.getBooleanExtra(KEY_ONE_SHOT, true);
         String dsuSlot = intent.getStringExtra(KEY_DSU_SLOT);
         String publicKey = intent.getStringExtra(KEY_PUBKEY);
 
@@ -384,9 +390,9 @@
         boolean enabled = false;
 
         if (mInstallTask != null && mInstallTask.isCompleted()) {
-            enabled = mInstallTask.commit();
+            enabled = mInstallTask.commit(mOneShot);
         } else if (isDynamicSystemInstalled()) {
-            enabled = mDynSystem.setEnable(true, true);
+            enabled = mDynSystem.setEnable(true, mOneShot);
         } else {
             Log.e(TAG, "Trying to reboot to AOT while there is no complete installation");
             return;
@@ -439,12 +445,16 @@
     private void executeNotifyIfInUseCommand() {
         switch (getStatus()) {
             case STATUS_IN_USE:
-                startForeground(NOTIFICATION_ID,
-                        buildNotification(STATUS_IN_USE, CAUSE_NOT_SPECIFIED));
+                if (!mHideNotification) {
+                    startForeground(NOTIFICATION_ID,
+                            buildNotification(STATUS_IN_USE, CAUSE_NOT_SPECIFIED));
+                }
                 break;
             case STATUS_READY:
-                startForeground(NOTIFICATION_ID,
-                        buildNotification(STATUS_READY, CAUSE_NOT_SPECIFIED));
+                if (!mHideNotification) {
+                    startForeground(NOTIFICATION_ID,
+                            buildNotification(STATUS_READY, CAUSE_NOT_SPECIFIED));
+                }
                 break;
             case STATUS_IN_PROGRESS:
                 break;
@@ -454,6 +464,16 @@
         }
     }
 
+    private void executeHideNotificationCommand() {
+        mHideNotification = true;
+        switch (getStatus()) {
+            case STATUS_IN_USE:
+            case STATUS_READY:
+                stopForeground(STOP_FOREGROUND_REMOVE);
+                break;
+        }
+    }
+
     private void resetTaskAndStop() {
         resetTaskAndStop(/* removeNotification= */ false);
     }
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index a41399f..42b620a 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -803,7 +803,7 @@
         return mIsCompleted;
     }
 
-    boolean commit() {
-        return mDynSystem.setEnable(true, true);
+    boolean commit(boolean oneShot) {
+        return mDynSystem.setEnable(true, oneShot);
     }
 }
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
index 64e42cc..b522729 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
@@ -16,6 +16,8 @@
 
 package com.android.dynsystem;
 
+import static android.os.image.DynamicSystemClient.KEY_KEYGUARD_USE_DEFAULT_STRINGS;
+
 import android.app.Activity;
 import android.app.KeyguardManager;
 import android.content.Context;
@@ -47,10 +49,7 @@
         KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
 
         if (km != null) {
-            String title = getString(R.string.keyguard_title);
-            String description = getString(R.string.keyguard_description);
-            Intent intent = km.createConfirmDeviceCredentialIntent(title, description);
-
+            Intent intent = createConfirmDeviceCredentialIntent(km);
             if (intent == null) {
                 Log.d(TAG, "This device is not protected by a password/pin");
                 startInstallationService();
@@ -63,6 +62,23 @@
         }
     }
 
+    private Intent createConfirmDeviceCredentialIntent(KeyguardManager km) {
+        final boolean useDefaultStrings =
+                getIntent().getBooleanExtra(KEY_KEYGUARD_USE_DEFAULT_STRINGS, false);
+        final String title;
+        final String description;
+        if (useDefaultStrings) {
+            // Use default strings provided by keyguard manager
+            title = null;
+            description = null;
+        } else {
+            // Use custom strings provided by DSU
+            title = getString(R.string.keyguard_title);
+            description = getString(R.string.keyguard_description);
+        }
+        return km.createConfirmDeviceCredentialIntent(title, description);
+    }
+
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
diff --git a/packages/PrintSpooler/tests/outofprocess/Android.bp b/packages/PrintSpooler/tests/outofprocess/Android.bp
index 69a1d7f..ef0d122 100644
--- a/packages/PrintSpooler/tests/outofprocess/Android.bp
+++ b/packages/PrintSpooler/tests/outofprocess/Android.bp
@@ -31,7 +31,7 @@
     libs: ["android.test.runner.stubs"],
     static_libs: [
         "androidx.test.rules",
-        "ub-uiautomator",
+        "androidx.test.uiautomator_uiautomator",
         "mockito-target-minus-junit4",
         "print-test-util-lib",
     ],
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
index 132545b..1509b70 100644
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
+++ b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
@@ -35,15 +35,15 @@
 import android.print.test.services.FirstPrintService;
 import android.print.test.services.PrinterDiscoverySessionCallbacks;
 import android.print.test.services.StubbablePrinterDiscoverySession;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiObjectNotFoundException;
-import android.support.test.uiautomator.UiSelector;
-import android.support.test.uiautomator.Until;
 import android.util.Log;
 
 import androidx.test.filters.LargeTest;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject;
+import androidx.test.uiautomator.UiObjectNotFoundException;
+import androidx.test.uiautomator.UiSelector;
+import androidx.test.uiautomator.Until;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
diff --git a/packages/SettingsLib/OWNERS b/packages/SettingsLib/OWNERS
index 1b3020a..81340f5 100644
--- a/packages/SettingsLib/OWNERS
+++ b/packages/SettingsLib/OWNERS
@@ -3,9 +3,8 @@
 edgarwang@google.com
 evanlaird@google.com
 juliacr@google.com
-lijun@google.com
-songchenxi@google.com
 yantingyang@google.com
+ykhung@google.com
 
 # Exempt resource files (because they are in a flat directory and too hard to manage via OWNERS)
 per-file *.xml=*
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 a7e95b5..eaf3229 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.io.PrintWriter
 import java.util.concurrent.ConcurrentHashMap
 import java.util.concurrent.atomic.AtomicBoolean
 import kotlinx.coroutines.CoroutineDispatcher
@@ -485,6 +486,14 @@
         return availableClocks[targetClockId]?.provider?.createClock(settings)
     }
 
+    fun dump(pw: PrintWriter, args: Array<out String>) {
+        pw.println("ClockRegistry:")
+        pw.println("  settings = $settings")
+        for ((id, info) in availableClocks) {
+            pw.println("  availableClocks[$id] = $info")
+        }
+    }
+
     private data class ClockInfo(
         val metadata: ClockMetadata,
         var provider: ClockProvider?,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 0326b6d..5ba0ad6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -294,11 +294,11 @@
 
     public void dump(PrintWriter pw, String[] args) {
         pw.println("KeyguardClockSwitch:");
-        pw.println("  mSmallClockFrame: " + mSmallClockFrame);
-        pw.println("  mSmallClockFrame.alpha: " + mSmallClockFrame.getAlpha());
-        pw.println("  mLargeClockFrame: " + mLargeClockFrame);
-        pw.println("  mLargeClockFrame.alpha: " + mLargeClockFrame.getAlpha());
-        pw.println("  mStatusArea: " + mStatusArea);
-        pw.println("  mDisplayedClockSize: " + mDisplayedClockSize);
+        pw.println("  mSmallClockFrame = " + mSmallClockFrame);
+        pw.println("  mSmallClockFrame.alpha = " + mSmallClockFrame.getAlpha());
+        pw.println("  mLargeClockFrame = " + mLargeClockFrame);
+        pw.println("  mLargeClockFrame.alpha = " + mLargeClockFrame.getAlpha());
+        pw.println("  mStatusArea = " + mStatusArea);
+        pw.println("  mDisplayedClockSize = " + mDisplayedClockSize);
     }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 25d1792..ad333b7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -493,7 +493,7 @@
         if (mDateWeatherView != null) {
             mUiExecutor.execute(() -> {
                 mDateWeatherView.setVisibility(
-                        clockHasCustomWeatherDataDisplay() ? View.GONE : View.VISIBLE);
+                        clockHasCustomWeatherDataDisplay() ? View.INVISIBLE : View.VISIBLE);
             });
         }
     }
@@ -519,9 +519,10 @@
 
     @Override
     public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
-        pw.println("currentClockSizeLarge=" + (mCurrentClockSize == LARGE));
-        pw.println("mCanShowDoubleLineClock=" + mCanShowDoubleLineClock);
+        pw.println("currentClockSizeLarge: " + (mCurrentClockSize == LARGE));
+        pw.println("mCanShowDoubleLineClock: " + mCanShowDoubleLineClock);
         mView.dump(pw, args);
+        mClockRegistry.dump(pw, args);
         ClockController clock = getClock();
         if (clock != null) {
             clock.dump(pw);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 33a8224..cc64389 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1259,6 +1259,9 @@
      * for bridging the gap while the migration is active.
      */
     private void handleFaceHelp(int msgId, String helpString) {
+        if (mFaceAcquiredInfoIgnoreList.contains(msgId)) {
+            return;
+        }
         Assert.isMainThread();
         mLogger.logFaceAuthHelpMsg(msgId, helpString);
         for (int i = 0; i < mCallbacks.size(); i++) {
@@ -4370,7 +4373,7 @@
      * Cancels all operations in the scheduler if it is hung for 10 seconds.
      */
     public void startBiometricWatchdog() {
-        if (mFaceManager != null) {
+        if (mFaceManager != null && !isFaceAuthInteractorEnabled()) {
             mFaceManager.scheduleWatchdog();
         }
         if (mFpm != null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index 6e98a18..cde8ff7 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -33,6 +33,7 @@
 
 import dagger.Module;
 import dagger.Provides;
+
 import kotlinx.coroutines.CoroutineDispatcher;
 import kotlinx.coroutines.CoroutineScope;
 
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index ffacf01..f01749a 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -229,7 +229,7 @@
     /** Whether to inflate the bouncer view on a background thread. */
     // TODO(b/273341787): Tracking Bug
     @JvmField
-    val PREVENT_BYPASS_KEYGUARD = unreleasedFlag(230, "prevent_bypass_keyguard", teamfood = true)
+    val PREVENT_BYPASS_KEYGUARD = releasedFlag(230, "prevent_bypass_keyguard")
 
     /** Whether to use a new data source for intents to run on keyguard dismissal. */
     @JvmField
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
index c4fc883..5f6098b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -34,6 +34,7 @@
 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.domain.interactor.KeyguardTransitionInteractor
 import com.android.systemui.keyguard.shared.model.AcquiredAuthenticationStatus
 import com.android.systemui.keyguard.shared.model.AuthenticationStatus
 import com.android.systemui.keyguard.shared.model.DetectionStatus
@@ -133,6 +134,7 @@
     private val alternateBouncerInteractor: AlternateBouncerInteractor,
     @FaceDetectTableLog private val faceDetectLog: TableLogBuffer,
     @FaceAuthTableLog private val faceAuthLog: TableLogBuffer,
+    private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
     dumpManager: DumpManager,
 ) : DeviceEntryFaceAuthRepository, Dumpable {
     private var authCancellationSignal: CancellationSignal? = null
@@ -211,6 +213,13 @@
         observeFaceAuthGatingChecks()
         observeFaceDetectGatingChecks()
         observeFaceAuthResettingConditions()
+        listenForSchedulingWatchdog()
+    }
+
+    private fun listenForSchedulingWatchdog() {
+        keyguardTransitionInteractor.anyStateToGoneTransition
+            .onEach { faceManager?.scheduleWatchdog() }
+            .launchIn(applicationScope)
     }
 
     private fun observeFaceAuthResettingConditions() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
index 4cc0410..fa42114 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
@@ -49,6 +49,7 @@
 import android.support.v4.media.MediaMetadataCompat
 import android.text.TextUtils
 import android.util.Log
+import android.util.Pair as APair
 import androidx.media.utils.MediaConstants
 import com.android.internal.logging.InstanceId
 import com.android.keyguard.KeyguardUpdateMonitor
@@ -217,6 +218,13 @@
     private var smartspaceSession: SmartspaceSession? = null
     private var allowMediaRecommendations = allowMediaRecommendations(context)
 
+    private val artworkWidth =
+        context.resources.getDimensionPixelSize(
+            com.android.internal.R.dimen.config_mediaMetadataBitmapMaxSize
+        )
+    private val artworkHeight =
+        context.resources.getDimensionPixelSize(R.dimen.qs_media_session_height_expanded)
+
     /** Check whether this notification is an RCN */
     private fun isRemoteCastNotification(sbn: StatusBarNotification): Boolean {
         return sbn.notification.extras.containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE)
@@ -1250,9 +1258,21 @@
             return null
         }
 
-        val source = ImageDecoder.createSource(context.getContentResolver(), uri)
+        val source = ImageDecoder.createSource(context.contentResolver, uri)
         return try {
-            ImageDecoder.decodeBitmap(source) { decoder, _, _ ->
+            ImageDecoder.decodeBitmap(source) { decoder, info, _ ->
+                val width = info.size.width
+                val height = info.size.height
+                val scale =
+                    MediaDataUtils.getScaleFactor(
+                        APair(width, height),
+                        APair(artworkWidth, artworkHeight)
+                    )
+
+                // Downscale if needed
+                if (scale != 0f && scale < 1) {
+                    decoder.setTargetSize((scale * width).toInt(), (scale * height).toInt())
+                }
                 decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE
             }
         } catch (e: IOException) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index cb1f12cf..40027a1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -56,6 +56,7 @@
 import android.os.Trace;
 import android.text.TextUtils;
 import android.util.Log;
+import android.util.Pair;
 import android.util.TypedValue;
 import android.view.Gravity;
 import android.view.View;
@@ -122,6 +123,11 @@
 import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.time.SystemClock;
 
+import dagger.Lazy;
+
+import kotlin.Triple;
+import kotlin.Unit;
+
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.List;
@@ -129,10 +135,6 @@
 
 import javax.inject.Inject;
 
-import dagger.Lazy;
-import kotlin.Triple;
-import kotlin.Unit;
-
 /**
  * A view controller used for Media Playback.
  */
@@ -1000,18 +1002,9 @@
 
         int width = drawable.getIntrinsicWidth();
         int height = drawable.getIntrinsicHeight();
-        if (width == 0 || height == 0 || targetWidth == 0 || targetHeight == 0) {
-            return;
-        }
-
-        float scale;
-        if ((width / (float) height) > (targetWidth / (float) targetHeight)) {
-            // Drawable is wider than target view, scale to match height
-            scale = targetHeight / (float) height;
-        } else {
-            // Drawable is taller than target view, scale to match width
-            scale = targetWidth / (float) width;
-        }
+        float scale = MediaDataUtils.getScaleFactor(new Pair(width, height),
+                new Pair(targetWidth, targetHeight));
+        if (scale == 0) return;
         transitionDrawable.setLayerSize(layer, (int) (scale * width), (int) (scale * height));
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaDataUtils.java b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaDataUtils.java
index e95106e..0239d36 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaDataUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaDataUtils.java
@@ -22,6 +22,7 @@
 import android.content.pm.PackageManager;
 import android.os.Bundle;
 import android.text.TextUtils;
+import android.util.Pair;
 
 import androidx.core.math.MathUtils;
 import androidx.media.utils.MediaConstants;
@@ -87,4 +88,32 @@
         }
         return null;
     }
+
+    /**
+     * Calculate a scale factor that will allow the input to fill the target size.
+     *
+     * @param input width, height of the input view
+     * @param target width, height of the target view
+     * @return the scale factor; 0 if any given dimension is 0
+     */
+    public static float getScaleFactor(Pair<Integer, Integer> input,
+            Pair<Integer, Integer> target) {
+        float width = (float) input.first;
+        float height = (float) input.second;
+
+        float targetWidth = (float) target.first;
+        float targetHeight = (float) target.second;
+
+        if (width == 0 || height == 0 || targetWidth == 0 || targetHeight == 0) {
+            return 0f;
+        }
+
+        if ((width / height) > (targetWidth / targetHeight)) {
+            // Input is wider than target view, scale to match height
+            return targetHeight / height;
+        } else {
+            // Input is taller than target view, scale to match width
+            return targetWidth / width;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index cffe45f..0a188e0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.recents;
 
+import static android.content.Intent.ACTION_PACKAGE_ADDED;
 import static android.content.Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST;
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 import static android.view.MotionEvent.ACTION_CANCEL;
@@ -48,6 +49,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
+import android.content.pm.ResolveInfo;
 import android.graphics.Region;
 import android.hardware.input.InputManager;
 import android.hardware.input.InputManagerGlobal;
@@ -114,6 +116,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
@@ -393,20 +396,29 @@
     private final BroadcastReceiver mLauncherStateChangedReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            StringBuilder extraComponentList = new StringBuilder(" components: ");
-            if (intent.hasExtra(EXTRA_CHANGED_COMPONENT_NAME_LIST)) {
-                String[] comps = intent.getStringArrayExtra(EXTRA_CHANGED_COMPONENT_NAME_LIST);
-                if (comps != null) {
-                    for (String c : comps) {
-                        extraComponentList.append(c).append(", ");
-                    }
+            // If adding, bind immediately
+            if (Objects.equals(intent.getAction(), ACTION_PACKAGE_ADDED)) {
+                updateEnabledAndBinding();
+                return;
+            }
+
+            // ACTION_PACKAGE_CHANGED
+            String[] compsList = intent.getStringArrayExtra(EXTRA_CHANGED_COMPONENT_NAME_LIST);
+            if (compsList == null) {
+                return;
+            }
+
+            // Only rebind for TouchInteractionService component from launcher
+            ResolveInfo ri = context.getPackageManager()
+                    .resolveService(new Intent(ACTION_QUICKSTEP), 0);
+            String interestingComponent = ri.serviceInfo.name;
+            for (String component : compsList) {
+                if (interestingComponent.equals(component)) {
+                    Log.i(TAG_OPS, "Rebinding for component [" + component + "] change");
+                    updateEnabledAndBinding();
+                    return;
                 }
             }
-            Log.d(TAG_OPS, "launcherStateChanged intent: " + intent + extraComponentList);
-            updateEnabledState();
-
-            // Reconnect immediately, instead of waiting for resume to arrive.
-            startConnectionToCurrentUser();
         }
     };
 
@@ -621,8 +633,7 @@
         screenLifecycle.addObserver(mScreenLifecycleObserver);
         wakefulnessLifecycle.addObserver(mWakefulnessLifecycleObserver);
         // Connect to the service
-        updateEnabledState();
-        startConnectionToCurrentUser();
+        updateEnabledAndBinding();
 
         // Listen for assistant changes
         assistUtils.registerVoiceInteractionSessionListener(mVoiceInteractionSessionListener);
@@ -644,6 +655,10 @@
     private void dispatchNavigationBarSurface() {
         try {
             if (mOverviewProxy != null) {
+                // Catch all for cases where the surface is no longer valid
+                if (mNavigationBarSurface != null && !mNavigationBarSurface.isValid()) {
+                    mNavigationBarSurface = null;
+                }
                 mOverviewProxy.onNavigationBarSurface(mNavigationBarSurface);
             }
         } catch (RemoteException e) {
@@ -651,6 +666,11 @@
         }
     }
 
+    private void updateEnabledAndBinding() {
+        updateEnabledState();
+        startConnectionToCurrentUser();
+    }
+
     private void updateSystemUiStateFlags() {
         final NavigationBar navBarFragment =
                 mNavBarControllerLazy.get().getDefaultNavigationBar();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 222a0f4f..a4a7d4c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -129,6 +129,7 @@
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
@@ -330,6 +331,7 @@
     private final PulseExpansionHandler mPulseExpansionHandler;
     private final KeyguardBypassController mKeyguardBypassController;
     private final KeyguardUpdateMonitor mUpdateMonitor;
+    private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
     private final ConversationNotificationManager mConversationNotificationManager;
     private final AuthController mAuthController;
     private final MediaHierarchyManager mMediaHierarchyManager;
@@ -747,7 +749,8 @@
             DumpManager dumpManager,
             KeyguardLongPressViewModel keyguardLongPressViewModel,
             KeyguardInteractor keyguardInteractor,
-            ActivityStarter activityStarter) {
+            ActivityStarter activityStarter,
+            KeyguardFaceAuthInteractor keyguardFaceAuthInteractor) {
         mInteractionJankMonitor = interactionJankMonitor;
         keyguardStateController.addCallback(new KeyguardStateController.Callback() {
             @Override
@@ -891,6 +894,7 @@
         mScreenOffAnimationController = screenOffAnimationController;
         mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
         mLastDownEvents = new NPVCDownEventState.Buffer(MAX_DOWN_EVENT_BUFFER_SIZE);
+        mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
 
         int currentMode = navigationModeController.addListener(
                 mode -> mIsGestureNavigation = QuickStepContract.isGesturalMode(mode));
@@ -2764,6 +2768,7 @@
                     mShadeLog.v("onMiddleClicked on Keyguard, mDozingOnDown: false");
                     // Try triggering face auth, this "might" run. Check
                     // KeyguardUpdateMonitor#shouldListenForFace to see when face auth won't run.
+                    mKeyguardFaceAuthInteractor.onNotificationPanelClicked();
                     boolean didFaceAuthRun = mUpdateMonitor.requestFaceAuth(
                             FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
index 82bd45c..6322edf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
@@ -24,6 +24,10 @@
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.expansionChanges
@@ -40,22 +44,26 @@
 import com.android.systemui.statusbar.policy.headsUpEvents
 import com.android.systemui.util.settings.SecureSettings
 import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
-import javax.inject.Inject
-import kotlin.time.Duration
-import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.collectLatest
 import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emitAll
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flowOn
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.transformLatest
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.yield
+import javax.inject.Inject
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
 
 /**
  * Filters low priority and privacy-sensitive notifications from the lockscreen, and hides section
@@ -69,6 +77,7 @@
     private val headsUpManager: HeadsUpManager,
     private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider,
     private val keyguardRepository: KeyguardRepository,
+    private val keyguardTransitionRepository: KeyguardTransitionRepository,
     private val notifPipelineFlags: NotifPipelineFlags,
     @Application private val scope: CoroutineScope,
     private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider,
@@ -99,21 +108,46 @@
     }
 
     private suspend fun trackUnseenNotificationsWhileUnlocked() {
+        // Whether or not we're actively tracking unseen notifications to mark them as seen when
+        // appropriate.
+        val isTrackingUnseen: Flow<Boolean> =
+            keyguardRepository.isKeyguardShowing
+                // transformLatest so that we can cancel listening to keyguard transitions once
+                // isKeyguardShowing changes (after a successful transition to the keyguard).
+                .transformLatest { isShowing ->
+                    if (isShowing) {
+                        // If the keyguard is showing, we're not tracking unseen.
+                        emit(false)
+                    } else {
+                        // If the keyguard stops showing, then start tracking unseen notifications.
+                        emit(true)
+                        // If the screen is turning off, stop tracking, but if that transition is
+                        // cancelled, then start again.
+                        emitAll(
+                            keyguardTransitionRepository.transitions
+                                .map { step -> !step.isScreenTurningOff }
+                        )
+                    }
+                }
+                // Prevent double emit of `false` caused by transition to AOD, followed by keyguard
+                // showing
+                .distinctUntilChanged()
+
         // Use collectLatest so that trackUnseenNotifications() is cancelled when the keyguard is
         // showing again
-        var clearUnseenOnUnlock = false
-        keyguardRepository.isKeyguardShowing.collectLatest { isKeyguardShowing ->
-            if (isKeyguardShowing) {
+        var clearUnseenOnBeginTracking = false
+        isTrackingUnseen.collectLatest { trackingUnseen ->
+            if (!trackingUnseen) {
                 // Wait for the user to spend enough time on the lock screen before clearing unseen
                 // set when unlocked
                 awaitTimeSpentNotDozing(SEEN_TIMEOUT)
-                clearUnseenOnUnlock = true
+                clearUnseenOnBeginTracking = true
             } else {
-                unseenNotifFilter.invalidateList("keyguard no longer showing")
-                if (clearUnseenOnUnlock) {
-                    clearUnseenOnUnlock = false
+                if (clearUnseenOnBeginTracking) {
+                    clearUnseenOnBeginTracking = false
                     unseenNotifications.clear()
                 }
+                unseenNotifFilter.invalidateList("keyguard no longer showing")
                 trackUnseenNotifications()
             }
         }
@@ -142,7 +176,10 @@
     }
 
     private suspend fun clearUnseenNotificationsWhenShadeIsExpanded() {
-        statusBarStateController.expansionChanges.collect { isExpanded ->
+        statusBarStateController.expansionChanges.collectLatest { isExpanded ->
+            // Give keyguard events time to propagate, in case this expansion is part of the
+            // keyguard transition and not the user expanding the shade
+            yield()
             if (isExpanded) {
                 unseenNotifications.clear()
             }
@@ -276,3 +313,6 @@
         private val SEEN_TIMEOUT = 5.seconds
     }
 }
+
+private val TransitionStep.isScreenTurningOff: Boolean get() =
+    transitionState == TransitionState.STARTED && to != KeyguardState.GONE
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index eaa1455..b3d2461 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -35,6 +35,7 @@
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl
 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
 import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
 import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxyImpl
 import com.android.systemui.statusbar.pipeline.mobile.util.SubscriptionManagerProxy
@@ -53,6 +54,9 @@
 import dagger.Provides
 import dagger.multibindings.ClassKey
 import dagger.multibindings.IntoMap
+import kotlinx.coroutines.flow.Flow
+import java.util.function.Supplier
+import javax.inject.Named
 
 @Module
 abstract class StatusBarPipelineModule {
@@ -115,6 +119,17 @@
 
         @Provides
         @SysUISingleton
+        @Named(FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON)
+        fun provideFirstMobileSubShowingNetworkTypeIconProvider(
+            mobileIconsViewModel: MobileIconsViewModel,
+        ): Supplier<Flow<Boolean>> {
+            return Supplier<Flow<Boolean>> {
+                mobileIconsViewModel.firstMobileSubShowingNetworkTypeIcon
+            }
+        }
+
+        @Provides
+        @SysUISingleton
         @WifiInputLog
         fun provideWifiInputLogBuffer(factory: LogBufferFactory): LogBuffer {
             return factory.create("WifiInputLog", 50)
@@ -168,5 +183,8 @@
         fun provideVerboseMobileViewLogBuffer(factory: LogBufferFactory): LogBuffer {
             return factory.create("VerboseMobileViewLog", 100)
         }
+
+        const val FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON =
+            "FirstMobileSubShowingNetworkTypeIcon"
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
index 9af5e83..40b8c90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
@@ -32,6 +32,9 @@
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
@@ -63,22 +66,36 @@
             }
             .stateIn(scope, SharingStarted.WhileSubscribed(), listOf())
 
+    private val firstMobileSubViewModel: StateFlow<MobileIconViewModelCommon?> =
+        subscriptionIdsFlow
+            .map {
+                if (it.isEmpty()) {
+                    null
+                } else {
+                    // Mobile icons get reversed by [StatusBarIconController], so the last element
+                    // in this list will show up visually first.
+                    commonViewModelForSub(it.last())
+                }
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), null)
+
+    /**
+     * A flow that emits `true` if the mobile sub that's displayed first visually is showing its
+     * network type icon and `false` otherwise.
+     */
+    val firstMobileSubShowingNetworkTypeIcon: StateFlow<Boolean> =
+        firstMobileSubViewModel
+            .flatMapLatest { firstMobileSubViewModel ->
+                firstMobileSubViewModel?.networkTypeIcon?.map { it != null } ?: flowOf(false)
+            }
+            .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
     init {
         scope.launch { subscriptionIdsFlow.collect { removeInvalidModelsFromCache(it) } }
     }
 
     fun viewModelForSub(subId: Int, location: StatusBarLocation): LocationBasedMobileViewModel {
-        val common =
-            mobileIconSubIdCache[subId]
-                ?: MobileIconViewModel(
-                        subId,
-                        interactor.createMobileConnectionInteractorForSubId(subId),
-                        airplaneModeInteractor,
-                        constants,
-                        scope,
-                    )
-                    .also { mobileIconSubIdCache[subId] = it }
-
+        val common = commonViewModelForSub(subId)
         return LocationBasedMobileViewModel.viewModelForLocation(
             common,
             statusBarPipelineFlags,
@@ -87,6 +104,18 @@
         )
     }
 
+    private fun commonViewModelForSub(subId: Int): MobileIconViewModelCommon {
+        return mobileIconSubIdCache[subId]
+            ?: MobileIconViewModel(
+                    subId,
+                    interactor.createMobileConnectionInteractorForSubId(subId),
+                    airplaneModeInteractor,
+                    constants,
+                    scope,
+                )
+                .also { mobileIconSubIdCache[subId] = it }
+    }
+
     private fun removeInvalidModelsFromCache(subIds: List<Int>) {
         val subIdsToRemove = mobileIconSubIdCache.keys.filter { !subIds.contains(it) }
         subIdsToRemove.forEach { mobileIconSubIdCache.remove(it) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
index 9e8c814..e819c4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
@@ -36,7 +36,6 @@
 import kotlinx.coroutines.InternalCoroutinesApi
 import kotlinx.coroutines.awaitCancellation
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.launch
 
@@ -64,6 +63,7 @@
         val activityOutView = view.requireViewById<ImageView>(R.id.wifi_out)
         val activityContainerView = view.requireViewById<View>(R.id.inout_container)
         val airplaneSpacer = view.requireViewById<View>(R.id.wifi_airplane_spacer)
+        val signalSpacer = view.requireViewById<View>(R.id.wifi_signal_spacer)
 
         view.isVisible = true
         iconView.isVisible = true
@@ -133,6 +133,12 @@
                     }
                 }
 
+                launch {
+                    viewModel.isSignalSpacerVisible.distinctUntilChanged().collect { visible ->
+                        signalSpacer.isVisible = visible
+                    }
+                }
+
                 try {
                     awaitCancellation()
                 } finally {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
index c9a0786..d9c2144 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
@@ -30,8 +30,8 @@
 import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
 import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS
 import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK
-import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
+import com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule.Companion.FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON
 import com.android.systemui.statusbar.pipeline.dagger.WifiTableLog
 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
@@ -39,7 +39,9 @@
 import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
 import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
+import java.util.function.Supplier
 import javax.inject.Inject
+import javax.inject.Named
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
@@ -53,24 +55,24 @@
 /**
  * Models the UI state for the status bar wifi icon.
  *
- * This class exposes three view models, one per status bar location: [home], [keyguard], and [qs].
- * In order to get the UI state for the wifi icon, you must use one of those view models (whichever
- * is correct for your location).
- *
- * Internally, this class maintains the current state of the wifi icon and notifies those three view
- * models of any changes.
+ * This is a singleton so that we don't have duplicate logs and should *not* be used directly to
+ * control views. Instead, use an instance of [LocationBasedWifiViewModel]. See
+ * [LocationBasedWifiViewModel.viewModelForLocation].
  */
 @SysUISingleton
 class WifiViewModel
 @Inject
 constructor(
     airplaneModeViewModel: AirplaneModeViewModel,
+    // TODO(b/238425913): The wifi icon shouldn't need to consume mobile information. A
+    //  container-level view model should do the work instead.
+    @Named(FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON)
+    shouldShowSignalSpacerProvider: Supplier<Flow<Boolean>>,
     connectivityConstants: ConnectivityConstants,
     private val context: Context,
     @WifiTableLog wifiTableLogBuffer: TableLogBuffer,
     interactor: WifiInteractor,
     @Application private val scope: CoroutineScope,
-    statusBarPipelineFlags: StatusBarPipelineFlags,
     wifiConstants: WifiConstants,
 ) : WifiViewModelCommon {
     /** Returns the icon to use based on the given network. */
@@ -183,6 +185,8 @@
     override val isAirplaneSpacerVisible: Flow<Boolean> =
         airplaneModeViewModel.isAirplaneModeIconVisible
 
+    override val isSignalSpacerVisible: Flow<Boolean> = shouldShowSignalSpacerProvider.get()
+
     companion object {
         @StringRes
         @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelCommon.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelCommon.kt
index eccf023..617e192 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelCommon.kt
@@ -39,4 +39,7 @@
 
     /** True if the airplane spacer view should be visible. */
     val isAirplaneSpacerVisible: Flow<Boolean>
+
+    /** True if the spacer between the wifi icon and the RAT icon should be visible. */
+    val isSignalSpacerVisible: Flow<Boolean>
 }
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index b9f8dd94..fc906de 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -391,7 +391,7 @@
         listenerArgumentCaptor.getValue().onCurrentClockChanged();
 
         mExecutor.runAllReady();
-        assertEquals(View.GONE, mFakeDateView.getVisibility());
+        assertEquals(View.INVISIBLE, mFakeDateView.getVisibility());
     }
 
     @Test
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
index f21ea3d..fc75d47 100644
--- 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
@@ -44,11 +44,14 @@
 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.domain.interactor.KeyguardTransitionInteractor
 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.KeyguardState
 import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.keyguard.shared.model.WakeSleepReason
 import com.android.systemui.keyguard.shared.model.WakefulnessModel
 import com.android.systemui.keyguard.shared.model.WakefulnessState
@@ -119,6 +122,7 @@
     private lateinit var faceLockoutResetCallback: ArgumentCaptor<FaceManager.LockoutResetCallback>
     private lateinit var testDispatcher: TestDispatcher
 
+    private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
     private lateinit var testScope: TestScope
     private lateinit var fakeUserRepository: FakeUserRepository
     private lateinit var authStatus: FlowValue<AuthenticationStatus?>
@@ -189,6 +193,9 @@
         val systemClock = FakeSystemClock()
         val faceAuthBuffer = TableLogBuffer(10, "face auth", systemClock)
         val faceDetectBuffer = TableLogBuffer(10, "face detect", systemClock)
+        keyguardTransitionRepository = FakeKeyguardTransitionRepository()
+        val keyguardTransitionInteractor =
+            KeyguardTransitionInteractor(keyguardTransitionRepository)
         return DeviceEntryFaceAuthRepositoryImpl(
             mContext,
             fmOverride,
@@ -207,6 +214,7 @@
             alternateBouncerInteractor,
             faceDetectBuffer,
             faceAuthBuffer,
+            keyguardTransitionInteractor,
             dumpManager,
         )
     }
@@ -772,6 +780,50 @@
             }
         }
 
+    @Test
+    fun schedulesFaceManagerWatchdogWhenKeyguardIsGoneFromDozing() =
+        testScope.runTest {
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(from = KeyguardState.DOZING, to = KeyguardState.GONE)
+            )
+
+            runCurrent()
+            verify(faceManager).scheduleWatchdog()
+        }
+
+    @Test
+    fun schedulesFaceManagerWatchdogWhenKeyguardIsGoneFromAod() =
+        testScope.runTest {
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(from = KeyguardState.AOD, to = KeyguardState.GONE)
+            )
+
+            runCurrent()
+            verify(faceManager).scheduleWatchdog()
+        }
+
+    @Test
+    fun schedulesFaceManagerWatchdogWhenKeyguardIsGoneFromLockscreen() =
+        testScope.runTest {
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE)
+            )
+
+            runCurrent()
+            verify(faceManager).scheduleWatchdog()
+        }
+
+    @Test
+    fun schedulesFaceManagerWatchdogWhenKeyguardIsGoneFromBouncer() =
+        testScope.runTest {
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(from = KeyguardState.PRIMARY_BOUNCER, to = KeyguardState.GONE)
+            )
+
+            runCurrent()
+            verify(faceManager).scheduleWatchdog()
+        }
+
     private suspend fun TestScope.testGatingCheckForFaceAuth(gatingCheckModifier: () -> Unit) {
         initCollectors()
         allPreconditionsToRunFaceAuthAreTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/util/MediaDataUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/util/MediaDataUtilsTest.kt
new file mode 100644
index 0000000..86f3062
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/util/MediaDataUtilsTest.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.media.controls.util
+
+import android.testing.AndroidTestingRunner
+import android.util.Pair as APair
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class MediaDataUtilsTest : SysuiTestCase() {
+
+    @Test
+    fun testScaleFactor_zeroInput_returnsZero() {
+        val input = APair(0, 0)
+        val target = APair(100, 100)
+
+        val scale = MediaDataUtils.getScaleFactor(input, target)
+        assertThat(scale).isEqualTo(0f)
+    }
+
+    @Test
+    fun testScaleFactor_tooWide_scaleDown() {
+        val input = APair(400, 200)
+        val target = APair(100, 100)
+
+        val scale = MediaDataUtils.getScaleFactor(input, target)
+        assertThat(scale).isEqualTo(0.5f)
+    }
+
+    @Test
+    fun testScaleFactor_tooTall_scaleDown() {
+        val input = APair(200, 400)
+        val target = APair(100, 100)
+
+        val scale = MediaDataUtils.getScaleFactor(input, target)
+        assertThat(scale).isEqualTo(0.5f)
+    }
+
+    @Test
+    fun testScaleFactor_lessWide_scaleUp() {
+        val input = APair(50, 100)
+        val target = APair(100, 100)
+
+        val scale = MediaDataUtils.getScaleFactor(input, target)
+        assertThat(scale).isEqualTo(2f)
+    }
+
+    @Test
+    fun testScaleFactor_lessTall_scaleUp() {
+        val input = APair(100, 50)
+        val target = APair(100, 100)
+
+        val scale = MediaDataUtils.getScaleFactor(input, target)
+        assertThat(scale).isEqualTo(2f)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 4bfd6a2..068d933 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -303,6 +303,7 @@
     protected ArgumentCaptor<NotificationStackScrollLayout.OnEmptySpaceClickListener>
             mEmptySpaceClickListenerCaptor;
     @Mock protected ActivityStarter mActivityStarter;
+    @Mock protected KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
 
     protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
     protected KeyguardInteractor mKeyguardInteractor;
@@ -600,7 +601,8 @@
                 mDumpManager,
                 mKeyuardLongPressViewModel,
                 mKeyguardInteractor,
-                mActivityStarter);
+                mActivityStarter,
+                mKeyguardFaceAuthInteractor);
         mNotificationPanelViewController.initDependencies(
                 mCentralSurfaces,
                 null,
@@ -667,7 +669,7 @@
                 mFeatureFlags,
                 mInteractionJankMonitor,
                 mShadeLog,
-                mock(KeyguardFaceAuthInteractor.class)
+                mKeyguardFaceAuthInteractor
         );
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 2db9c97..600fb5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -930,6 +930,7 @@
         mTouchHandler.onTouch(mock(View.class), mDownMotionEvent);
         mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0);
 
+        verify(mKeyguardFaceAuthInteractor).onNotificationPanelClicked();
         verify(mUpdateMonitor).requestFaceAuth(
                 FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
index 8109e24..c2a2a40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
@@ -25,6 +25,10 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.advanceTimeBy
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.notification.NotifPipelineFlags
@@ -69,6 +73,7 @@
     private val headsUpManager: HeadsUpManager = mock()
     private val keyguardNotifVisibilityProvider: KeyguardNotificationVisibilityProvider = mock()
     private val keyguardRepository = FakeKeyguardRepository()
+    private val keyguardTransitionRepository = FakeKeyguardTransitionRepository()
     private val notifPipelineFlags: NotifPipelineFlags = mock()
     private val notifPipeline: NotifPipeline = mock()
     private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider = mock()
@@ -118,6 +123,33 @@
     }
 
     @Test
+    fun unseenFilterStopsMarkingSeenNotifWhenTransitionToAod() {
+        whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
+
+        // GIVEN: Keyguard is not showing, shade is not expanded, and a notification is present
+        keyguardRepository.setKeyguardShowing(false)
+        whenever(statusBarStateController.isExpanded).thenReturn(false)
+        runKeyguardCoordinatorTest {
+            val fakeEntry = NotificationEntryBuilder().build()
+            collectionListener.onEntryAdded(fakeEntry)
+
+            // WHEN: The device transitions to AOD
+            keyguardTransitionRepository.sendTransitionStep(
+                TransitionStep(to = KeyguardState.AOD, transitionState = TransitionState.STARTED),
+            )
+            testScheduler.runCurrent()
+
+            // WHEN: The shade is expanded
+            whenever(statusBarStateController.isExpanded).thenReturn(true)
+            statusBarStateListener.onExpandedChanged(true)
+            testScheduler.runCurrent()
+
+            // THEN: The notification is still treated as "unseen" and is not filtered out.
+            assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isFalse()
+        }
+    }
+
+    @Test
     fun unseenFilter_headsUpMarkedAsSeen() {
         whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
 
@@ -373,6 +405,7 @@
                 headsUpManager,
                 keyguardNotifVisibilityProvider,
                 keyguardRepository,
+                keyguardTransitionRepository,
                 notifPipelineFlags,
                 testScope.backgroundScope,
                 sectionHeaderVisibilityProvider,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
index 3ced7b2..b2bbcfd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
@@ -49,6 +49,8 @@
             FIVE_G_OVERRIDE_KEY to TelephonyIcons.NR_5G,
         )
 
+    private val interactorCache: MutableMap<Int, FakeMobileIconInteractor> = mutableMapOf()
+
     override val isDefaultConnectionFailed = MutableStateFlow(false)
 
     override val filteredSubscriptions = MutableStateFlow<List<SubscriptionModel>>(listOf())
@@ -75,7 +77,15 @@
 
     /** Always returns a new fake interactor */
     override fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor {
-        return FakeMobileIconInteractor(tableLogBuffer)
+        return FakeMobileIconInteractor(tableLogBuffer).also { interactorCache[subId] = it }
+    }
+
+    /**
+     * Returns the most recently created interactor for the given subId, or null if an interactor
+     * has never been created for that sub.
+     */
+    fun getInteractorForSubId(subId: Int): FakeMobileIconInteractor? {
+        return interactorCache[subId]
     }
 
     companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
index 01bec87..f8e1aa9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
 
 import androidx.test.filters.SmallTest
+import com.android.settingslib.mobile.TelephonyIcons
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.statusbar.phone.StatusBarLocation
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
@@ -24,6 +25,7 @@
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
 import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger
 import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
@@ -135,8 +137,179 @@
             assertThat(underTest.mobileIconSubIdCache).containsExactly(2, model2.commonImpl)
         }
 
+    @Test
+    fun firstMobileSubShowingNetworkTypeIcon_noSubs_false() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job =
+                underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            interactor.filteredSubscriptions.value = emptyList()
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
+    fun firstMobileSubShowingNetworkTypeIcon_oneSub_notShowingRat_false() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job =
+                underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            interactor.filteredSubscriptions.value = listOf(SUB_1)
+            // The unknown icon group doesn't show a RAT
+            interactor.getInteractorForSubId(1)!!.networkTypeIconGroup.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
+    fun firstMobileSubShowingNetworkTypeIcon_oneSub_showingRat_true() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job =
+                underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            interactor.filteredSubscriptions.value = listOf(SUB_1)
+            // The 3G icon group will show a RAT
+            interactor.getInteractorForSubId(1)!!.networkTypeIconGroup.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
+
+            assertThat(latest).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun firstMobileSubShowingNetworkTypeIcon_updatesAsSubUpdates() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job =
+                underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            interactor.filteredSubscriptions.value = listOf(SUB_1)
+            val sub1Interactor = interactor.getInteractorForSubId(1)!!
+
+            sub1Interactor.networkTypeIconGroup.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
+            assertThat(latest).isTrue()
+
+            sub1Interactor.networkTypeIconGroup.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+            assertThat(latest).isFalse()
+
+            sub1Interactor.networkTypeIconGroup.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.LTE)
+            assertThat(latest).isTrue()
+
+            job.cancel()
+        }
+
+    @Test
+    fun firstMobileSubShowingNetworkTypeIcon_multipleSubs_lastSubNotShowingRat_false() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job =
+                underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+            interactor.getInteractorForSubId(1)?.networkTypeIconGroup?.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
+            interactor.getInteractorForSubId(2)!!.networkTypeIconGroup.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
+    fun firstMobileSubShowingNetworkTypeIcon_multipleSubs_lastSubShowingRat_true() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job =
+                underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+            interactor.getInteractorForSubId(1)?.networkTypeIconGroup?.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+            interactor.getInteractorForSubId(2)!!.networkTypeIconGroup.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
+
+            assertThat(latest).isTrue()
+            job.cancel()
+        }
+
+    @Test
+    fun firstMobileSubShowingNetworkTypeIcon_subListUpdates_valAlsoUpdates() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job =
+                underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+            interactor.getInteractorForSubId(1)?.networkTypeIconGroup?.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+            interactor.getInteractorForSubId(2)!!.networkTypeIconGroup.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
+
+            assertThat(latest).isTrue()
+
+            // WHEN the sub list gets new subscriptions where the last subscription is not showing
+            // the network type icon
+            interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2, SUB_3)
+            interactor.getInteractorForSubId(3)!!.networkTypeIconGroup.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+
+            // THEN the flow updates
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
+    fun firstMobileSubShowingNetworkTypeIcon_subListReorders_valAlsoUpdates() =
+        testScope.runTest {
+            var latest: Boolean? = null
+            val job =
+                underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+            interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+            // Immediately switch the order so that we've created both interactors
+            interactor.filteredSubscriptions.value = listOf(SUB_2, SUB_1)
+            val sub1Interactor = interactor.getInteractorForSubId(1)!!
+            val sub2Interactor = interactor.getInteractorForSubId(2)!!
+
+            interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+            sub1Interactor.networkTypeIconGroup.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+            sub2Interactor.networkTypeIconGroup.value =
+                NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
+            assertThat(latest).isTrue()
+
+            // WHEN sub1 becomes last and sub1 has no network type icon
+            interactor.filteredSubscriptions.value = listOf(SUB_2, SUB_1)
+
+            // THEN the flow updates
+            assertThat(latest).isFalse()
+
+            // WHEN sub2 becomes last and sub2 has a network type icon
+            interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+
+            // THEN the flow updates
+            assertThat(latest).isTrue()
+
+            job.cancel()
+        }
+
     companion object {
         private val SUB_1 = SubscriptionModel(subscriptionId = 1, isOpportunistic = false)
         private val SUB_2 = SubscriptionModel(subscriptionId = 2, isOpportunistic = false)
+        private val SUB_3 = SubscriptionModel(subscriptionId = 3, isOpportunistic = false)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
index 5c19108..0d51af2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
@@ -50,6 +50,7 @@
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.MutableStateFlow
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -98,12 +99,12 @@
         val viewModelCommon =
             WifiViewModel(
                 airplaneModeViewModel,
+                shouldShowSignalSpacerProvider = { MutableStateFlow(false) },
                 connectivityConstants,
                 context,
                 tableLogBuffer,
                 interactor,
                 scope,
-                statusBarPipelineFlags,
                 wifiConstants,
             )
         viewModel =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
index ffe990b..e6724d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
@@ -27,7 +27,6 @@
 import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
 import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS
 import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK
-import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
 import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
@@ -46,6 +45,7 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.yield
@@ -66,7 +66,6 @@
 
     private lateinit var underTest: WifiViewModel
 
-    @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
     @Mock private lateinit var tableLogBuffer: TableLogBuffer
     @Mock private lateinit var connectivityConstants: ConnectivityConstants
     @Mock private lateinit var wifiConstants: WifiConstants
@@ -121,12 +120,12 @@
             underTest =
                 WifiViewModel(
                     airplaneModeViewModel,
+                    shouldShowSignalSpacerProvider = { MutableStateFlow(false) },
                     connectivityConstants,
                     context,
                     tableLogBuffer,
                     interactor,
                     scope,
-                    statusBarPipelineFlags,
                     wifiConstants,
                 )
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index 802e360..0e303b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -39,8 +39,8 @@
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.runBlocking
@@ -53,7 +53,6 @@
 import org.mockito.MockitoAnnotations
 
 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 class WifiViewModelTest : SysuiTestCase() {
 
@@ -68,6 +67,7 @@
     private lateinit var wifiRepository: FakeWifiRepository
     private lateinit var interactor: WifiInteractor
     private lateinit var airplaneModeViewModel: AirplaneModeViewModel
+    private val shouldShowSignalSpacerProviderFlow = MutableStateFlow(false)
     private lateinit var scope: CoroutineScope
 
     @Before
@@ -473,6 +473,34 @@
             job.cancel()
         }
 
+    @Test
+    fun signalSpacer_firstSubNotShowingNetworkTypeIcon_outputsFalse() =
+        runBlocking(IMMEDIATE) {
+            var latest: Boolean? = null
+            val job = underTest.isSignalSpacerVisible.onEach { latest = it }.launchIn(this)
+
+            shouldShowSignalSpacerProviderFlow.value = false
+            yield()
+
+            assertThat(latest).isFalse()
+
+            job.cancel()
+        }
+
+    @Test
+    fun signalSpacer_firstSubIsShowingNetworkTypeIcon_outputsTrue() =
+        runBlocking(IMMEDIATE) {
+            var latest: Boolean? = null
+            val job = underTest.isSignalSpacerVisible.onEach { latest = it }.launchIn(this)
+
+            shouldShowSignalSpacerProviderFlow.value = true
+            yield()
+
+            assertThat(latest).isTrue()
+
+            job.cancel()
+        }
+
     private fun createAndSetViewModel() {
         // [WifiViewModel] creates its flows as soon as it's instantiated, and some of those flow
         // creations rely on certain config values that we mock out in individual tests. This method
@@ -480,12 +508,12 @@
         underTest =
             WifiViewModel(
                 airplaneModeViewModel,
+                { shouldShowSignalSpacerProviderFlow },
                 connectivityConstants,
                 context,
                 tableLogBuffer,
                 interactor,
                 scope,
-                statusBarPipelineFlags,
                 wifiConstants,
             )
     }
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldRemoteFilter.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldRemoteFilter.kt
index 0b019d1..3041888 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldRemoteFilter.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldRemoteFilter.kt
@@ -35,7 +35,7 @@
 
     private var inProgress = false
 
-    private var processedProgress: Float = 0.0f
+    private var processedProgress: Float = 1.0f
         set(newProgress) {
             if (inProgress) {
                 logCounter({ "$TAG#filtered_progress" }, newProgress)
diff --git a/services/backup/java/com/android/server/backup/BackupAndRestoreFeatureFlags.java b/services/backup/java/com/android/server/backup/BackupAndRestoreFeatureFlags.java
index 1990fe2..98aebdd 100644
--- a/services/backup/java/com/android/server/backup/BackupAndRestoreFeatureFlags.java
+++ b/services/backup/java/com/android/server/backup/BackupAndRestoreFeatureFlags.java
@@ -77,4 +77,19 @@
                 /* name= */ "full_backup_utils_route_buffer_size_bytes",
                 /* defaultValue= */ 32 * 1024); // 32 KB
     }
+
+    /**
+     * Retrieves the value of the flag
+     * "unified_restore_continue_after_transport_failure_in_kv_restore".
+     * If true, Unified restore task will continue to next package if key-value restore of a
+     * package fails due to Transport-level failure. See b/128499560 for more context.
+     */
+    @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
+    public static boolean getUnifiedRestoreContinueAfterTransportFailureInKvRestore() {
+        return DeviceConfig.getBoolean(
+                NAMESPACE,
+                /* name= */
+                "unified_restore_continue_after_transport_failure_in_kv_restore",
+                /* defaultValue= */ true);
+    }
 }
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 18e28de..1656b6f 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -57,6 +57,7 @@
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.BackupAndRestoreFeatureFlags;
 import com.android.server.backup.BackupRestoreTask;
 import com.android.server.backup.BackupUtils;
 import com.android.server.backup.OperationStorage;
@@ -168,11 +169,13 @@
     private final BackupEligibilityRules mBackupEligibilityRules;
 
     @VisibleForTesting
-    PerformUnifiedRestoreTask(UserBackupManagerService backupManagerService) {
+    PerformUnifiedRestoreTask(
+            UserBackupManagerService backupManagerService,
+            TransportConnection transportConnection) {
         mListener = null;
         mAgentTimeoutParameters = null;
         mOperationStorage = null;
-        mTransportConnection = null;
+        mTransportConnection = transportConnection;
         mTransportManager = null;
         mEphemeralOpToken = 0;
         mUserId = 0;
@@ -731,13 +734,18 @@
                             ParcelFileDescriptor.MODE_TRUNCATE);
 
             if (transport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) {
-                // Transport-level failure, so we wind everything up and
-                // terminate the restore operation.
+                // Transport-level failure. This failure could be specific to package currently in
+                // restore.
                 Slog.e(TAG, "Error getting restore data for " + packageName);
                 EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
                 stage.close();
                 downloadFile.delete();
-                executeNextState(UnifiedRestoreState.FINAL);
+                UnifiedRestoreState nextState =
+                        BackupAndRestoreFeatureFlags
+                                .getUnifiedRestoreContinueAfterTransportFailureInKvRestore()
+                                ? UnifiedRestoreState.RUNNING_QUEUE
+                                : UnifiedRestoreState.FINAL;
+                executeNextState(nextState);
                 return;
             }
 
@@ -1358,6 +1366,7 @@
         executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
     }
 
+    @VisibleForTesting
     void executeNextState(UnifiedRestoreState nextState) {
         if (MORE_DEBUG) {
             Slog.i(TAG, " => executing next step on "
@@ -1369,6 +1378,26 @@
         backupManagerService.getBackupHandler().sendMessage(msg);
     }
 
+    @VisibleForTesting
+    UnifiedRestoreState getCurrentUnifiedRestoreStateForTesting() {
+        return mState;
+    }
+
+    @VisibleForTesting
+    void setCurrentUnifiedRestoreStateForTesting(UnifiedRestoreState state) {
+        mState = state;
+    }
+
+    @VisibleForTesting
+    void setStateDirForTesting(File stateDir) {
+        mStateDir = stateDir;
+    }
+
+    @VisibleForTesting
+    void initiateOneRestoreForTesting(PackageInfo app, long appVersionCode) {
+        initiateOneRestore(app, appVersionCode);
+    }
+
     // restore observer support
     void sendStartRestore(int numPackages) {
         if (mObserver != null) {
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 291c0587..9644642 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -151,15 +151,14 @@
     }
 
     void onCameraAccessBlocked(int appUid) {
-        synchronized (mVirtualDeviceManagerLock) {
-            for (int i = 0; i < mVirtualDevices.size(); i++) {
-                CharSequence deviceName = mVirtualDevices.valueAt(i).getDisplayName();
-                mVirtualDevices.valueAt(i).showToastWhereUidIsRunning(appUid,
-                        getContext().getString(
-                                com.android.internal.R.string.vdm_camera_access_denied,
-                                deviceName),
-                        Toast.LENGTH_LONG, Looper.myLooper());
-            }
+        ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
+        for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
+            VirtualDeviceImpl virtualDevice = virtualDevicesSnapshot.get(i);
+            virtualDevice.showToastWhereUidIsRunning(appUid,
+                    getContext().getString(
+                            com.android.internal.R.string.vdm_camera_access_denied,
+                            virtualDevice.getDisplayName()),
+                    Toast.LENGTH_LONG, Looper.myLooper());
         }
     }
 
@@ -265,6 +264,16 @@
         cdm.removeOnAssociationsChangedListener(mCdmAssociationListener);
     }
 
+    private ArrayList<VirtualDeviceImpl> getVirtualDevicesSnapshot() {
+        synchronized (mVirtualDeviceManagerLock) {
+            ArrayList<VirtualDeviceImpl> virtualDevices = new ArrayList<>(mVirtualDevices.size());
+            for (int i = 0; i < mVirtualDevices.size(); i++) {
+                virtualDevices.add(mVirtualDevices.valueAt(i));
+            }
+            return virtualDevices;
+        }
+    }
+
     class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub {
 
         private final VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback =
@@ -314,6 +323,17 @@
             Objects.requireNonNull(activityListener);
             Objects.requireNonNull(soundEffectListener);
 
+            final UserHandle userHandle = getCallingUserHandle();
+            final CameraAccessController cameraAccessController =
+                    getCameraAccessController(userHandle);
+            final int deviceId = sNextUniqueIndex.getAndIncrement();
+            final Consumer<ArraySet<Integer>> runningAppsChangedCallback =
+                    runningUids -> notifyRunningAppsChanged(deviceId, runningUids);
+            VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(),
+                    associationInfo, VirtualDeviceManagerService.this, token, callingUid,
+                    deviceId, cameraAccessController,
+                    mPendingTrampolineCallback, activityListener,
+                    soundEffectListener, runningAppsChangedCallback, params);
             synchronized (mVirtualDeviceManagerLock) {
                 if (mVirtualDevices.size() == 0) {
                     final long callindId = Binder.clearCallingIdentity();
@@ -323,21 +343,9 @@
                         Binder.restoreCallingIdentity(callindId);
                     }
                 }
-
-                final UserHandle userHandle = getCallingUserHandle();
-                final CameraAccessController cameraAccessController =
-                        getCameraAccessController(userHandle);
-                final int deviceId = sNextUniqueIndex.getAndIncrement();
-                final Consumer<ArraySet<Integer>> runningAppsChangedCallback =
-                        runningUids -> notifyRunningAppsChanged(deviceId, runningUids);
-                VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(),
-                        associationInfo, VirtualDeviceManagerService.this, token, callingUid,
-                        deviceId, cameraAccessController,
-                        mPendingTrampolineCallback, activityListener,
-                        soundEffectListener, runningAppsChangedCallback, params);
                 mVirtualDevices.put(deviceId, virtualDevice);
-                return virtualDevice;
             }
+            return virtualDevice;
         }
 
         @Override // Binder call
@@ -399,12 +407,11 @@
             if (displayId == Display.INVALID_DISPLAY || displayId == Display.DEFAULT_DISPLAY) {
                 return Context.DEVICE_ID_DEFAULT;
             }
-            synchronized (mVirtualDeviceManagerLock) {
-                for (int i = 0; i < mVirtualDevices.size(); i++) {
-                    VirtualDeviceImpl virtualDevice = mVirtualDevices.valueAt(i);
-                    if (virtualDevice.isDisplayOwnedByVirtualDevice(displayId)) {
-                        return virtualDevice.getDeviceId();
-                    }
+            ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
+            for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
+                VirtualDeviceImpl virtualDevice = virtualDevicesSnapshot.get(i);
+                if (virtualDevice.isDisplayOwnedByVirtualDevice(displayId)) {
+                    return virtualDevice.getDeviceId();
                 }
             }
             return Context.DEVICE_ID_DEFAULT;
@@ -496,10 +503,9 @@
                 return;
             }
             fout.println("Created virtual devices: ");
-            synchronized (mVirtualDeviceManagerLock) {
-                for (int i = 0; i < mVirtualDevices.size(); i++) {
-                    mVirtualDevices.valueAt(i).dump(fd, fout, args);
-                }
+            ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
+            for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
+                virtualDevicesSnapshot.get(i).dump(fd, fout, args);
             }
         }
     }
@@ -516,33 +522,30 @@
 
         @Override
         public int getDeviceOwnerUid(int deviceId) {
+            VirtualDeviceImpl virtualDevice;
             synchronized (mVirtualDeviceManagerLock) {
-                VirtualDeviceImpl virtualDevice = mVirtualDevices.get(deviceId);
-                return virtualDevice != null ? virtualDevice.getOwnerUid() : Process.INVALID_UID;
+                virtualDevice = mVirtualDevices.get(deviceId);
             }
+            return virtualDevice != null ? virtualDevice.getOwnerUid() : Process.INVALID_UID;
         }
 
         @Override
         public @Nullable VirtualSensor getVirtualSensor(int deviceId, int handle) {
+            VirtualDeviceImpl virtualDevice;
             synchronized (mVirtualDeviceManagerLock) {
-                VirtualDeviceImpl virtualDevice = mVirtualDevices.get(deviceId);
-                if (virtualDevice != null) {
-                    return virtualDevice.getVirtualSensorByHandle(handle);
-                }
+                virtualDevice = mVirtualDevices.get(deviceId);
             }
-            return null;
+            return virtualDevice != null ? virtualDevice.getVirtualSensorByHandle(handle) : null;
         }
 
         @Override
         public @NonNull ArraySet<Integer> getDeviceIdsForUid(int uid) {
+            ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
             ArraySet<Integer> result = new ArraySet<>();
-            synchronized (mVirtualDeviceManagerLock) {
-                int size = mVirtualDevices.size();
-                for (int i = 0; i < size; i++) {
-                    VirtualDeviceImpl device = mVirtualDevices.valueAt(i);
-                    if (device.isAppRunningOnVirtualDevice(uid)) {
-                        result.add(device.getDeviceId());
-                    }
+            for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
+                VirtualDeviceImpl device = virtualDevicesSnapshot.get(i);
+                if (device.isAppRunningOnVirtualDevice(uid)) {
+                    result.add(device.getDeviceId());
                 }
             }
             return result;
@@ -630,12 +633,10 @@
 
         @Override
         public boolean isAppRunningOnAnyVirtualDevice(int uid) {
-            synchronized (mVirtualDeviceManagerLock) {
-                int size = mVirtualDevices.size();
-                for (int i = 0; i < size; i++) {
-                    if (mVirtualDevices.valueAt(i).isAppRunningOnVirtualDevice(uid)) {
-                        return true;
-                    }
+            ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
+            for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
+                if (virtualDevicesSnapshot.get(i).isAppRunningOnVirtualDevice(uid)) {
+                    return true;
                 }
             }
             return false;
@@ -643,12 +644,10 @@
 
         @Override
         public boolean isDisplayOwnedByAnyVirtualDevice(int displayId) {
-            synchronized (mVirtualDeviceManagerLock) {
-                int size = mVirtualDevices.size();
-                for (int i = 0; i < size; i++) {
-                    if (mVirtualDevices.valueAt(i).isDisplayOwnedByVirtualDevice(displayId)) {
-                        return true;
-                    }
+            ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
+            for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
+                if (virtualDevicesSnapshot.get(i).isDisplayOwnedByVirtualDevice(displayId)) {
+                    return true;
                 }
             }
             return false;
diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java
index c6f63dd..12ee131 100644
--- a/services/core/java/android/os/BatteryStatsInternal.java
+++ b/services/core/java/android/os/BatteryStatsInternal.java
@@ -39,12 +39,14 @@
     public static final int CPU_WAKEUP_SUBSYSTEM_UNKNOWN = -1;
     public static final int CPU_WAKEUP_SUBSYSTEM_ALARM = 1;
     public static final int CPU_WAKEUP_SUBSYSTEM_WIFI = 2;
+    public static final int CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER = 3;
 
     /** @hide */
     @IntDef(prefix = {"CPU_WAKEUP_SUBSYSTEM_"}, value = {
             CPU_WAKEUP_SUBSYSTEM_UNKNOWN,
             CPU_WAKEUP_SUBSYSTEM_ALARM,
             CPU_WAKEUP_SUBSYSTEM_WIFI,
+            CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER,
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface CpuWakeupSubsystem {
diff --git a/services/core/java/com/android/server/DockObserver.java b/services/core/java/com/android/server/DockObserver.java
index 3487613..5156c54 100644
--- a/services/core/java/com/android/server/DockObserver.java
+++ b/services/core/java/com/android/server/DockObserver.java
@@ -70,7 +70,7 @@
 
     private boolean mUpdatesStopped;
 
-    private final boolean mKeepDreamingWhenUndocking;
+    private final boolean mKeepDreamingWhenUnplugging;
     private final boolean mAllowTheaterModeWakeFromDock;
 
     private final List<ExtconStateConfig> mExtconStateConfigs;
@@ -167,8 +167,8 @@
         mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
         mAllowTheaterModeWakeFromDock = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_allowTheaterModeWakeFromDock);
-        mKeepDreamingWhenUndocking = context.getResources().getBoolean(
-                com.android.internal.R.bool.config_keepDreamingWhenUndocking);
+        mKeepDreamingWhenUnplugging = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_keepDreamingWhenUnplugging);
         mDeviceProvisionedObserver = new DeviceProvisionedObserver(mHandler);
 
         mExtconStateConfigs = loadExtconStateConfigs(context);
@@ -237,7 +237,7 @@
     }
 
     private boolean allowWakeFromDock() {
-        if (mKeepDreamingWhenUndocking) {
+        if (mKeepDreamingWhenUnplugging) {
             return false;
         }
         return (mAllowTheaterModeWakeFromDock
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 409f054..123cd328 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -27,6 +27,7 @@
 per-file **IpSec* = file:/services/core/java/com/android/server/vcn/OWNERS
 per-file *Location* = file:/services/core/java/com/android/server/location/OWNERS
 per-file *Network* = file:/services/core/java/com/android/server/net/OWNERS
+per-file *SoundTrigger* = file:/media/java/android/media/soundtrigger/OWNERS
 per-file *Storage* = file:/core/java/android/os/storage/OWNERS
 per-file *TimeUpdate* = file:/services/core/java/com/android/server/timezonedetector/OWNERS
 per-file DynamicSystemService.java = file:/packages/DynamicSystemInstallationService/OWNERS
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index e248007..ee18ed5 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1019,6 +1019,24 @@
         r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                 service, neededGrants, callingUid, callingProcessName, callingPackage));
 
+        // We want to allow scheduling user-initiated jobs when the app is running a
+        // foreground service that was started in the same conditions that allows for scheduling
+        // UI jobs. More explicitly, we want to allow scheduling UI jobs when the app is running
+        // an FGS that started when the app was in the TOP or a BAL-approved state.
+        final boolean isFgs = r.isForeground || r.fgRequired;
+        if (isFgs) {
+            // As of Android UDC, the conditions required for the while-in-use permissions
+            // are the same conditions that we want, so we piggyback on that logic.
+            // Use that as a shortcut if possible to avoid having to recheck all the conditions.
+            final boolean whileInUseAllowsUiJobScheduling =
+                    ActivityManagerService.doesReasonCodeAllowSchedulingUserInitiatedJobs(
+                            r.mAllowWhileInUsePermissionInFgsReason);
+            r.updateAllowUiJobScheduling(whileInUseAllowsUiJobScheduling
+                    || mAm.canScheduleUserInitiatedJobs(callingUid, callingPid, callingPackage));
+        } else {
+            r.updateAllowUiJobScheduling(false);
+        }
+
         if (fgRequired) {
             // We are now effectively running a foreground service.
             synchronized (mAm.mProcessStats.mLock) {
@@ -7362,26 +7380,12 @@
         } else {
             allowWhileInUse = REASON_UNKNOWN;
         }
-        // We want to allow scheduling user-initiated jobs when the app is running a
-        // foreground service that was started in the same conditions that allows for scheduling
-        // UI jobs. More explicitly, we want to allow scheduling UI jobs when the app is running
-        // an FGS that started when the app was in the TOP or a BAL-approved state.
-        // As of Android UDC, the conditions required for the while-in-use permissions
-        // are the same conditions that we want, so we piggyback on that logic.
-        // We use that as a shortcut if possible so we don't have to recheck all the conditions.
-        final boolean isFgs = r.isForeground || r.fgRequired;
-        if (isFgs) {
-            r.updateAllowUiJobScheduling(ActivityManagerService
-                    .doesReasonCodeAllowSchedulingUserInitiatedJobs(allowWhileInUse)
-                    || mAm.canScheduleUserInitiatedJobs(
-                            callingUid, callingPid, callingPackage, true));
-        } else {
-            r.updateAllowUiJobScheduling(false);
-        }
+        r.mAllowWhileInUsePermissionInFgsReason = allowWhileInUse;
     }
 
     void resetFgsRestrictionLocked(ServiceRecord r) {
         r.mAllowWhileInUsePermissionInFgs = false;
+        r.mAllowWhileInUsePermissionInFgsReason = REASON_DENIED;
         r.mAllowStartForeground = REASON_DENIED;
         r.mInfoAllowStartForeground = null;
         r.mInfoTempFgsAllowListReason = null;
@@ -7425,14 +7429,17 @@
 
         final int uidState = mAm.getUidStateLocked(callingUid);
         if (ret == REASON_DENIED) {
-            // Is the calling UID at PROCESS_STATE_TOP or above?
+            // Allow FGS while-in-use if the caller's process state is PROCESS_STATE_PERSISTENT,
+            // PROCESS_STATE_PERSISTENT_UI or PROCESS_STATE_TOP.
             if (uidState <= PROCESS_STATE_TOP) {
                 ret = getReasonCodeFromProcState(uidState);
             }
         }
 
         if (ret == REASON_DENIED) {
-            // Does the calling UID have any visible activity?
+            // Allow FGS while-in-use if the caller has visible activity.
+            // Here we directly check ActivityTaskManagerService, instead of checking
+            // PendingStartActivityUids in ActivityManagerService, which gives the same result.
             final boolean isCallingUidVisible = mAm.mAtmInternal.isUidForeground(callingUid);
             if (isCallingUidVisible) {
                 ret = REASON_UID_VISIBLE;
@@ -7440,7 +7447,8 @@
         }
 
         if (ret == REASON_DENIED) {
-            // Is the allow activity background start flag on?
+            // Allow FGS while-in-use if the background activity start flag is on. Because
+            // activity start can lead to FGS start in TOP state and obtain while-in-use.
             if (backgroundStartPrivileges.allowsBackgroundActivityStarts()) {
                 ret = REASON_START_ACTIVITY_FLAG;
             }
@@ -7449,6 +7457,7 @@
         if (ret == REASON_DENIED) {
             boolean isCallerSystem = false;
             final int callingAppId = UserHandle.getAppId(callingUid);
+            // Allow FGS while-in-use for a list of special UIDs.
             switch (callingAppId) {
                 case ROOT_UID:
                 case SYSTEM_UID:
@@ -7467,6 +7476,10 @@
         }
 
         if (ret == REASON_DENIED) {
+            // Allow FGS while-in-use if the WindowManager allows background activity start.
+            // This is mainly to get the 10 seconds grace period if any activity in the caller has
+            // either started or finished very recently. The binding flag
+            // BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS is also allowed by the check here.
             final Integer allowedType = mAm.mProcessList.searchEachLruProcessesLOSP(false, pr -> {
                 if (pr.uid == callingUid) {
                     if (pr.getWindowProcessController().areBackgroundFgsStartsAllowed()) {
@@ -7481,6 +7494,12 @@
         }
 
         if (ret == REASON_DENIED) {
+            // Allow FGS while-in-use if the caller UID is in ActivityManagerService's
+            // mFgsWhileInUseTempAllowList. This is a temp allowlist to allow FGS while-in-use. It
+            // is used when MediaSessionService's bluetooth button or play/resume/stop commands are
+            // issued. The typical temp allowlist duration is 10 seconds.
+            // This temp allowlist mechanism can also be called by other system_server internal
+            // components such as Telephone/VOIP if they want to start a FGS and get while-in-use.
             if (mAm.mInternal.isTempAllowlistedForFgsWhileInUse(callingUid)) {
                 return REASON_TEMP_ALLOWED_WHILE_IN_USE;
             }
@@ -7488,6 +7507,8 @@
 
         if (ret == REASON_DENIED) {
             if (targetProcess != null) {
+                // Allow FGS while-in-use if the caller of the instrumentation has
+                // START_ACTIVITIES_FROM_BACKGROUND permission.
                 ActiveInstrumentation instr = targetProcess.getActiveInstrumentation();
                 if (instr != null && instr.mHasBackgroundActivityStartsPermission) {
                     ret = REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
@@ -7496,6 +7517,9 @@
         }
 
         if (ret == REASON_DENIED) {
+            // Allow FGS while-in-use if the caller has START_ACTIVITIES_FROM_BACKGROUND
+            // permission, because starting an activity can lead to starting FGS from the TOP state
+            // and obtain while-in-use.
             if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
                     == PERMISSION_GRANTED) {
                 ret = REASON_BACKGROUND_ACTIVITY_PERMISSION;
@@ -7503,6 +7527,8 @@
         }
 
         if (ret == REASON_DENIED) {
+            // Allow FGS while-in-use if the caller is in the while-in-use allowlist. Right now
+            // AttentionService and SystemCaptionsService packageName are in this allowlist.
             if (verifyPackage(callingPackage, callingUid)) {
                 final boolean isAllowedPackage =
                         mAllowListWhileInUsePermissionInFgs.contains(callingPackage);
@@ -7517,7 +7543,7 @@
         }
 
         if (ret == REASON_DENIED) {
-            // Is the calling UID a device owner app?
+            // Allow FGS while-in-use if the caller is the device owner.
             final boolean isDeviceOwner = mAm.mInternal.isDeviceOwner(callingUid);
             if (isDeviceOwner) {
                 ret = REASON_DEVICE_OWNER;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b32f8c9..97d34b8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6189,8 +6189,8 @@
 
         final ProcessServiceRecord psr = pr.mServices;
         if (psr != null && psr.hasForegroundServices()) {
-            for (int s = psr.numberOfExecutingServices() - 1; s >= 0; --s) {
-                final ServiceRecord sr = psr.getExecutingServiceAt(s);
+            for (int s = psr.numberOfRunningServices() - 1; s >= 0; --s) {
+                final ServiceRecord sr = psr.getRunningServiceAt(s);
                 if (sr.isForeground && sr.mAllowUiJobScheduling) {
                     return true;
                 }
@@ -6205,12 +6205,7 @@
      * {@link android.app.job.JobInfo.Builder#setUserInitiated(boolean) user-initiated job}.
      */
     // TODO(262260570): log allow reason to an atom
-    private boolean canScheduleUserInitiatedJobs(int uid, int pid, String pkgName) {
-        return canScheduleUserInitiatedJobs(uid, pid, pkgName, false);
-    }
-
-    boolean canScheduleUserInitiatedJobs(int uid, int pid, String pkgName,
-            boolean skipWhileInUseCheck) {
+    boolean canScheduleUserInitiatedJobs(int uid, int pid, String pkgName) {
         synchronized (this) {
             final ProcessRecord processRecord;
             synchronized (mPidsSelfLocked) {
@@ -6240,7 +6235,7 @@
             // As of Android UDC, the conditions required to grant a while-in-use permission
             // covers the majority of those cases, and so we piggyback on that logic as the base.
             // Missing cases are added after.
-            if (!skipWhileInUseCheck && mServices.canAllowWhileInUsePermissionInFgsLocked(
+            if (mServices.canAllowWhileInUsePermissionInFgsLocked(
                     pid, uid, pkgName, processRecord, backgroundStartPrivileges)) {
                 return true;
             }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index e080a80..17a0d62 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -247,6 +247,10 @@
                     return runSendBroadcast(pw);
                 case "compact":
                     return runCompact(pw);
+                case "freeze":
+                    return runFreeze(pw);
+                case "unfreeze":
+                    return runUnfreeze(pw);
                 case "instrument":
                     getOutPrintWriter().println("Error: must be invoked through 'am instrument'.");
                     return -1;
@@ -1074,20 +1078,10 @@
         boolean isFullCompact = op.equals("full");
         boolean isSomeCompact = op.equals("some");
         if (isFullCompact || isSomeCompact) {
-            String processName = getNextArgRequired();
-            synchronized (mInternal.mProcLock) {
-                // Default to current user
-                int userId = mInterface.getCurrentUserId();
-                String userOpt = getNextOption();
-                if (userOpt != null && "--user".equals(userOpt)) {
-                    int inputUserId = UserHandle.parseUserArg(getNextArgRequired());
-                    if (inputUserId != UserHandle.USER_CURRENT) {
-                        userId = inputUserId;
-                    }
-                }
-                final int uid =
-                        mInternal.getPackageManagerInternal().getPackageUid(processName, 0, userId);
-                app = mInternal.getProcessRecordLocked(processName, uid);
+            app = getProcessFromShell();
+            if (app == null) {
+                getErrPrintWriter().println("Error: could not find process");
+                return -1;
             }
             pw.println("Process record found pid: " + app.mPid);
             if (isFullCompact) {
@@ -1143,6 +1137,93 @@
         return 0;
     }
 
+    @NeverCompile
+    int runFreeze(PrintWriter pw) throws RemoteException {
+        String freezerOpt = getNextOption();
+        boolean isSticky = false;
+        if (freezerOpt != null) {
+            isSticky = freezerOpt.equals("--sticky");
+        }
+        ProcessRecord app = getProcessFromShell();
+        if (app == null) {
+            getErrPrintWriter().println("Error: could not find process");
+            return -1;
+        }
+        pw.println("Freezing pid: " + app.mPid + " sticky=" + isSticky);
+        synchronized (mInternal) {
+            synchronized (mInternal.mProcLock) {
+                app.mOptRecord.setFreezeSticky(isSticky);
+                mInternal.mOomAdjuster.mCachedAppOptimizer.freezeAppAsyncInternalLSP(app, 0, true);
+            }
+        }
+        return 0;
+    }
+
+    @NeverCompile
+    int runUnfreeze(PrintWriter pw) throws RemoteException {
+        String freezerOpt = getNextOption();
+        boolean isSticky = false;
+        if (freezerOpt != null) {
+            isSticky = freezerOpt.equals("--sticky");
+        }
+        ProcessRecord app = getProcessFromShell();
+        if (app == null) {
+            getErrPrintWriter().println("Error: could not find process");
+            return -1;
+        }
+        pw.println("Unfreezing pid: " + app.mPid);
+        synchronized (mInternal) {
+            synchronized (mInternal.mProcLock) {
+                synchronized (mInternal.mOomAdjuster.mCachedAppOptimizer.mFreezerLock) {
+                    app.mOptRecord.setFreezeSticky(isSticky);
+                    mInternal.mOomAdjuster.mCachedAppOptimizer.unfreezeAppInternalLSP(app, 0,
+                            false);
+                }
+            }
+        }
+        return 0;
+    }
+
+    /**
+     * Parses from the shell the process name and user id if provided and provides the corresponding
+     * {@link ProcessRecord)} If no user is provided, it will fallback to current user.
+     * Example usage: {@code <processname> --user current} or {@code <processname>}
+     * @return process record of process, null if none found.
+     * @throws RemoteException
+     */
+    @NeverCompile
+    ProcessRecord getProcessFromShell() throws RemoteException {
+        ProcessRecord app;
+        String processName = getNextArgRequired();
+        synchronized (mInternal.mProcLock) {
+            // Default to current user
+            int userId = getUserIdFromShellOrFallback();
+            final int uid =
+                    mInternal.getPackageManagerInternal().getPackageUid(processName, 0, userId);
+            app = mInternal.getProcessRecordLocked(processName, uid);
+        }
+        return app;
+    }
+
+    /**
+     * @return User id from command line provided in the form of
+     *  {@code --user <userid|current|all>} and if the argument is not found it will fallback
+     *  to current user.
+     * @throws RemoteException
+     */
+    @NeverCompile
+    int getUserIdFromShellOrFallback() throws RemoteException {
+        int userId = mInterface.getCurrentUserId();
+        String userOpt = getNextOption();
+        if (userOpt != null && "--user".equals(userOpt)) {
+            int inputUserId = UserHandle.parseUserArg(getNextArgRequired());
+            if (inputUserId != UserHandle.USER_CURRENT) {
+                userId = inputUserId;
+            }
+        }
+        return userId;
+    }
+
     int runDumpHeap(PrintWriter pw) throws RemoteException {
         final PrintWriter err = getErrPrintWriter();
         boolean managed = true;
@@ -4061,6 +4142,14 @@
             pw.println("      Perform a native compaction for process with <pid>.");
             pw.println("      some: execute file compaction.");
             pw.println("      full: execute anon + file compaction.");
+            pw.println("  freeze [--sticky] <processname> [--user <USER_ID>]");
+            pw.println("      Freeze a process.");
+            pw.println("        --sticky: persists the frozen state for the process lifetime or");
+            pw.println("                  until an unfreeze is triggered via shell");
+            pw.println("  unfreeze [--sticky] <processname> [--user <USER_ID>]");
+            pw.println("      Unfreeze a process.");
+            pw.println("        --sticky: persists the unfrozen state for the process lifetime or");
+            pw.println("                  until a freeze is triggered via shell");
             pw.println("  instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]");
             pw.println("          [--user <USER_ID> | current]");
             pw.println("          [--no-hidden-api-checks [--no-test-api-access]]");
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index ed297d0..0744f75 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -108,8 +108,8 @@
 import com.android.server.power.stats.BatteryStatsImpl;
 import com.android.server.power.stats.BatteryUsageStatsProvider;
 import com.android.server.power.stats.BatteryUsageStatsStore;
-import com.android.server.power.stats.CpuWakeupStats;
 import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
+import com.android.server.power.stats.wakeups.CpuWakeupStats;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -515,13 +515,11 @@
         @Override
         public void noteCpuWakingActivity(int subsystem, long elapsedMillis, int... uids) {
             Objects.requireNonNull(uids);
-            mCpuWakeupStats.noteWakingActivity(subsystem, elapsedMillis, uids);
+            mHandler.post(() -> mCpuWakeupStats.noteWakingActivity(subsystem, elapsedMillis, uids));
         }
-
         @Override
         public void noteWakingSoundTrigger(long elapsedMillis, int uid) {
-            // TODO(b/267717665): Pipe to noteCpuWakingActivity once SoundTrigger starts using this.
-            Slog.w(TAG, "Sound trigger event dispatched to uid " + uid);
+            noteCpuWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER, elapsedMillis, uid);
         }
     }
 
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 8ad76cb..1426cfd 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -342,7 +342,7 @@
 
     private final ActivityManagerGlobalLock mProcLock;
 
-    private final Object mFreezerLock = new Object();
+    public final Object mFreezerLock = new Object();
 
     private final OnPropertiesChangedListener mOnFlagsChangedListener =
             new OnPropertiesChangedListener() {
@@ -763,8 +763,9 @@
                 pw.println("  Apps frozen: " + size);
                 for (int i = 0; i < size; i++) {
                     ProcessRecord app = mFrozenProcesses.valueAt(i);
-                    pw.println("    " + app.mOptRecord.getFreezeUnfreezeTime()
-                            + ": " + app.getPid() + " " + app.processName);
+                    pw.println("    " + app.mOptRecord.getFreezeUnfreezeTime() + ": " + app.getPid()
+                            + " " + app.processName
+                            + (app.mOptRecord.isFreezeSticky() ? " (sticky)" : ""));
                 }
 
                 if (!mPendingCompactionProcesses.isEmpty()) {
@@ -1283,12 +1284,26 @@
 
     @GuardedBy({"mAm", "mProcLock"})
     void freezeAppAsyncLSP(ProcessRecord app) {
+        freezeAppAsyncInternalLSP(app, mFreezerDebounceTimeout, false);
+    }
+
+    @GuardedBy({"mAm", "mProcLock"})
+    void freezeAppAsyncInternalLSP(ProcessRecord app, long delayMillis, boolean force) {
         final ProcessCachedOptimizerRecord opt = app.mOptRecord;
         if (opt.isPendingFreeze()) {
             // Skip redundant DO_FREEZE message
             return;
         }
 
+        if (opt.isFreezeSticky() && !force) {
+            if (DEBUG_FREEZER) {
+                Slog.d(TAG_AM,
+                        "Skip freezing because unfrozen state is sticky pid=" + app.getPid() + " "
+                                + app.processName);
+            }
+            return;
+        }
+
         if (mAm.mConstants.USE_MODERN_TRIM
                 && app.mState.getSetAdj() >= ProcessList.CACHED_APP_MIN_ADJ) {
             final IApplicationThread thread = app.getThread();
@@ -1301,9 +1316,8 @@
             }
         }
         mFreezeHandler.sendMessageDelayed(
-                mFreezeHandler.obtainMessage(
-                    SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app),
-                mFreezerDebounceTimeout);
+                mFreezeHandler.obtainMessage(SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app),
+                delayMillis);
         opt.setPendingFreeze(true);
         if (DEBUG_FREEZER) {
             Slog.d(TAG_AM, "Async freezing " + app.getPid() + " " + app.processName);
@@ -1311,9 +1325,19 @@
     }
 
     @GuardedBy({"mAm", "mProcLock", "mFreezerLock"})
-    void unfreezeAppInternalLSP(ProcessRecord app, @UnfreezeReason int reason) {
+    void unfreezeAppInternalLSP(ProcessRecord app, @UnfreezeReason int reason, boolean force) {
         final int pid = app.getPid();
         final ProcessCachedOptimizerRecord opt = app.mOptRecord;
+        boolean sticky = opt.isFreezeSticky();
+        if (sticky && !force) {
+            // Sticky freezes will not change their state unless forced out of it.
+            if (DEBUG_FREEZER) {
+                Slog.d(TAG_AM,
+                        "Skip unfreezing because frozen state is sticky pid=" + pid + " "
+                                + app.processName);
+            }
+            return;
+        }
         if (opt.isPendingFreeze()) {
             // Remove pending DO_FREEZE message
             mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);
@@ -1406,7 +1430,7 @@
     @GuardedBy({"mAm", "mProcLock"})
     void unfreezeAppLSP(ProcessRecord app, @UnfreezeReason int reason) {
         synchronized (mFreezerLock) {
-            unfreezeAppInternalLSP(app, reason);
+            unfreezeAppInternalLSP(app, reason, false);
         }
     }
 
@@ -2080,15 +2104,6 @@
 
             synchronized (mProcLock) {
                 pid = proc.getPid();
-                if (proc.mState.getCurAdj() < ProcessList.CACHED_APP_MIN_ADJ
-                        || opt.shouldNotFreeze()) {
-                    if (DEBUG_FREEZER) {
-                        Slog.d(TAG_AM, "Skipping freeze for process " + pid
-                                + " " + name + " curAdj = " + proc.mState.getCurAdj()
-                                + ", shouldNotFreeze = " + opt.shouldNotFreeze());
-                    }
-                    return;
-                }
 
                 if (mFreezerOverride) {
                     opt.setFreezerOverride(true);
diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
index f233107..e8c8f6d 100644
--- a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
+++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
@@ -75,6 +75,15 @@
     private boolean mFrozen;
 
     /**
+     * If set to true it will make the (un)freeze decision sticky which means that the freezer
+     * decision will remain the same unless a freeze is forced via {@link #mForceFreezeOps}.
+     * This property is usually set to true when external user wants to maintain a (un)frozen state
+     * after being applied.
+     */
+    @GuardedBy("mProcLock")
+    private boolean mFreezeSticky;
+
+    /**
      * Set to false after the process has been frozen.
      * Set to true after we have collected PSS for the frozen process.
      */
@@ -195,6 +204,15 @@
     void setFrozen(boolean frozen) {
         mFrozen = frozen;
     }
+    @GuardedBy("mProcLock")
+    void setFreezeSticky(boolean sticky) {
+        mFreezeSticky = sticky;
+    }
+
+    @GuardedBy("mProcLock")
+    boolean isFreezeSticky() {
+        return mFreezeSticky;
+    }
 
     boolean skipPSSCollectionBecauseFrozen() {
         boolean collected = mHasCollectedFrozenPSS;
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index edf0dbd..a875860 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -174,6 +174,8 @@
     // allow while-in-use permissions in foreground service or not.
     // while-in-use permissions in FGS started from background might be restricted.
     boolean mAllowWhileInUsePermissionInFgs;
+    @PowerExemptionManager.ReasonCode
+    int mAllowWhileInUsePermissionInFgsReason;
     // A copy of mAllowWhileInUsePermissionInFgs's value when the service is entering FGS state.
     boolean mAllowWhileInUsePermissionInFgsAtEntering;
     /** Allow scheduling user-initiated jobs from the background. */
@@ -609,6 +611,8 @@
         }
         pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs=");
                 pw.println(mAllowWhileInUsePermissionInFgs);
+        pw.print(prefix); pw.print("mAllowWhileInUsePermissionInFgsReason=");
+        pw.println(mAllowWhileInUsePermissionInFgsReason);
         pw.print(prefix); pw.print("allowUiJobScheduling="); pw.println(mAllowUiJobScheduling);
         pw.print(prefix); pw.print("recentCallingPackage=");
                 pw.println(mRecentCallingPackage);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index ac55f28..d1cbbfc 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -7053,13 +7053,16 @@
             return deviceSet.iterator().next();
         } else {
             // Multiple device selection is either:
+            //  - dock + one other device: give priority to dock in this case.
             //  - speaker + one other device: give priority to speaker in this case.
             //  - one A2DP device + another device: happens with duplicated output. In this case
             // retain the device on the A2DP output as the other must not correspond to an active
             // selection if not the speaker.
             //  - HDMI-CEC system audio mode only output: give priority to available item in order.
 
-            if (deviceSet.contains(AudioSystem.DEVICE_OUT_SPEAKER)) {
+            if (deviceSet.contains(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET)) {
+                return AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET;
+            } else if (deviceSet.contains(AudioSystem.DEVICE_OUT_SPEAKER)) {
                 return AudioSystem.DEVICE_OUT_SPEAKER;
             } else if (deviceSet.contains(AudioSystem.DEVICE_OUT_SPEAKER_SAFE)) {
                 // Note: DEVICE_OUT_SPEAKER_SAFE not present in getDeviceSetForStreamDirect
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 7802b9d..0e26d46 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -124,7 +124,7 @@
     private final boolean mDreamsEnabledByDefaultConfig;
     private final boolean mDreamsActivatedOnChargeByDefault;
     private final boolean mDreamsActivatedOnDockByDefault;
-    private final boolean mKeepDreamingWhenUndockedDefault;
+    private final boolean mKeepDreamingWhenUnpluggingDefault;
 
     private final CopyOnWriteArrayList<DreamManagerInternal.DreamManagerStateListener>
             mDreamManagerStateListeners = new CopyOnWriteArrayList<>();
@@ -236,8 +236,8 @@
         mDreamsActivatedOnDockByDefault = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault);
         mSettingsObserver = new SettingsObserver(mHandler);
-        mKeepDreamingWhenUndockedDefault = mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_keepDreamingWhenUndocking);
+        mKeepDreamingWhenUnpluggingDefault = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_keepDreamingWhenUnplugging);
     }
 
     @Override
@@ -311,7 +311,7 @@
             pw.println("mIsDocked=" + mIsDocked);
             pw.println("mIsCharging=" + mIsCharging);
             pw.println("mWhenToDream=" + mWhenToDream);
-            pw.println("mKeepDreamingWhenUndockedDefault=" + mKeepDreamingWhenUndockedDefault);
+            pw.println("mKeepDreamingWhenUnpluggingDefault=" + mKeepDreamingWhenUnpluggingDefault);
             pw.println("getDozeComponent()=" + getDozeComponent());
             pw.println();
 
@@ -340,11 +340,11 @@
         }
     }
 
-    private void reportKeepDreamingWhenUndockedChanged(boolean keepDreaming) {
+    private void reportKeepDreamingWhenUnpluggingChanged(boolean keepDreaming) {
         mHandler.post(() -> {
             for (DreamManagerInternal.DreamManagerStateListener listener
                     : mDreamManagerStateListeners) {
-                listener.onKeepDreamingWhenUndockedChanged(keepDreaming);
+                listener.onKeepDreamingWhenUnpluggingChanged(keepDreaming);
             }
         });
     }
@@ -600,8 +600,7 @@
             }
 
             mSystemDreamComponent = componentName;
-            reportKeepDreamingWhenUndockedChanged(shouldKeepDreamingWhenUndocked());
-
+            reportKeepDreamingWhenUnpluggingChanged(shouldKeepDreamingWhenUnplugging());
             // Switch dream if currently dreaming and not dozing.
             if (isDreamingInternal() && !isDozingInternal()) {
                 startDreamInternal(false /*doze*/, (mSystemDreamComponent == null ? "clear" : "set")
@@ -610,8 +609,8 @@
         }
     }
 
-    private boolean shouldKeepDreamingWhenUndocked() {
-        return mKeepDreamingWhenUndockedDefault && mSystemDreamComponent == null;
+    private boolean shouldKeepDreamingWhenUnplugging() {
+        return mKeepDreamingWhenUnpluggingDefault && mSystemDreamComponent == null;
     }
 
     private ComponentName getDefaultDreamComponentForUser(int userId) {
@@ -1057,7 +1056,7 @@
         public void registerDreamManagerStateListener(DreamManagerStateListener listener) {
             mDreamManagerStateListeners.add(listener);
             // Initialize the listener's state.
-            listener.onKeepDreamingWhenUndockedChanged(shouldKeepDreamingWhenUndocked());
+            listener.onKeepDreamingWhenUnpluggingChanged(shouldKeepDreamingWhenUnplugging());
         }
 
         @Override
diff --git a/services/core/java/com/android/server/inputmethod/OWNERS b/services/core/java/com/android/server/inputmethod/OWNERS
index 00cd700..6e5eb56 100644
--- a/services/core/java/com/android/server/inputmethod/OWNERS
+++ b/services/core/java/com/android/server/inputmethod/OWNERS
@@ -1,8 +1,8 @@
 set noparent
 
-ogunwale@google.com
+roosa@google.com
 yukawa@google.com
 tarandeep@google.com
-lumark@google.com
-roosa@google.com
-wilsonwu@google.com
+
+ogunwale@google.com #{LAST_RESORT_SUGGESTION}
+jjaggi@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1301cd47..ebcbfed 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3335,7 +3335,7 @@
             }
 
             checkCallerIsSameApp(pkg);
-            final boolean isSystemToast = isCallerSystemOrPhone()
+            final boolean isSystemToast = isCallerIsSystemOrSystemUi()
                     || PackageManagerService.PLATFORM_PACKAGE_NAME.equals(pkg);
             boolean isAppRenderedToast = (callback != null);
             if (!checkCanEnqueueToast(pkg, callingUid, displayId, isAppRenderedToast,
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 73be549..5f8efe2 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2641,6 +2641,7 @@
 
     private void setUserRestrictionInner(int userId, @NonNull String key, boolean value) {
         if (!UserRestrictionsUtils.isValidRestriction(key)) {
+            Slog.e(LOG_TAG, "Setting invalid restriction " + key);
             return;
         }
         synchronized (mRestrictionsLock) {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 2e8a150..e392c24 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -667,15 +667,15 @@
     // but the DreamService has not yet been told to start (it's an async process).
     private boolean mDozeStartInProgress;
 
-    // Whether to keep dreaming when the device is undocked.
-    private boolean mKeepDreamingWhenUndocked;
+    // Whether to keep dreaming when the device is unplugging.
+    private boolean mKeepDreamingWhenUnplugging;
 
     private final class DreamManagerStateListener implements
             DreamManagerInternal.DreamManagerStateListener {
         @Override
-        public void onKeepDreamingWhenUndockedChanged(boolean keepDreaming) {
+        public void onKeepDreamingWhenUnpluggingChanged(boolean keepDreaming) {
             synchronized (mLock) {
-                mKeepDreamingWhenUndocked = keepDreaming;
+                mKeepDreamingWhenUnplugging = keepDreaming;
             }
         }
     }
@@ -2504,14 +2504,12 @@
             return false;
         }
 
-        // Don't wake when undocking while dreaming if configured not to.
-        if (mKeepDreamingWhenUndocked
+        // Don't wake when unplugging while dreaming if configured not to.
+        if (mKeepDreamingWhenUnplugging
                 && getGlobalWakefulnessLocked() == WAKEFULNESS_DREAMING
-                && wasPowered && !mIsPowered
-                && oldPlugType == BatteryManager.BATTERY_PLUGGED_DOCK) {
+                && wasPowered && !mIsPowered) {
             return false;
         }
-
         // Don't wake when undocked from wireless charger.
         // See WirelessChargerDetector for justification.
         if (wasPowered && !mIsPowered
@@ -4477,7 +4475,7 @@
                     + mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig);
             pw.println("  mTheaterModeEnabled="
                     + mTheaterModeEnabled);
-            pw.println("  mKeepDreamingWhenUndocked=" + mKeepDreamingWhenUndocked);
+            pw.println("  mKeepDreamingWhenUnplugging=" + mKeepDreamingWhenUnplugging);
             pw.println("  mSuspendWhenScreenOffDueToProximityConfig="
                     + mSuspendWhenScreenOffDueToProximityConfig);
             pw.println("  mDreamsSupportedConfig=" + mDreamsSupportedConfig);
diff --git a/services/core/java/com/android/server/power/ShutdownCheckPoints.java b/services/core/java/com/android/server/power/ShutdownCheckPoints.java
index 32f1bcf..546dc81 100644
--- a/services/core/java/com/android/server/power/ShutdownCheckPoints.java
+++ b/services/core/java/com/android/server/power/ShutdownCheckPoints.java
@@ -295,11 +295,18 @@
         @Nullable
         String getProcessName() {
             try {
-                List<ActivityManager.RunningAppProcessInfo> runningProcesses =
-                        mActivityManager.getRunningAppProcesses();
-                for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) {
-                    if (processInfo.pid == mCallerProcessId) {
-                        return processInfo.processName;
+                List<ActivityManager.RunningAppProcessInfo> runningProcesses = null;
+                if (mActivityManager != null) {
+                    runningProcesses = mActivityManager.getRunningAppProcesses();
+                } else {
+                    Slog.v(TAG, "No ActivityManager available to find process name with pid="
+                            + mCallerProcessId);
+                }
+                if (runningProcesses != null) {
+                    for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) {
+                        if (processInfo.pid == mCallerProcessId) {
+                            return processInfo.processName;
+                        }
                     }
                 }
             } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index c2d4ac6..b1430e7 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -703,17 +703,20 @@
             // vibrate before shutting down
             Vibrator vibrator = new SystemVibrator(context);
             try {
-                vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
+                if (vibrator.hasVibrator()) {
+                    vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
+                    // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
+                    try {
+                        Thread.sleep(SHUTDOWN_VIBRATE_MS);
+                    } catch (InterruptedException unused) {
+                        // this is not critical and does not require logging
+                    }
+                }
             } catch (Exception e) {
                 // Failure to vibrate shouldn't interrupt shutdown.  Just log it.
                 Log.w(TAG, "Failed to vibrate during shutdown.", e);
             }
 
-            // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
-            try {
-                Thread.sleep(SHUTDOWN_VIBRATE_MS);
-            } catch (InterruptedException unused) {
-            }
         }
         // Shutdown power
         Log.i(TAG, "Performing low-level shutdown...");
diff --git a/services/core/java/com/android/server/power/stats/CpuWakeupStats.java b/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java
similarity index 91%
rename from services/core/java/com/android/server/power/stats/CpuWakeupStats.java
rename to services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java
index 231ffc6..1d63489 100644
--- a/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
+++ b/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java
@@ -14,9 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.server.power.stats;
+package com.android.server.power.stats.wakeups;
 
 import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM;
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER;
 import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
 import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI;
 
@@ -55,7 +56,8 @@
     private static final String TAG = "CpuWakeupStats";
 
     private static final String SUBSYSTEM_ALARM_STRING = "Alarm";
-    private static final String SUBSYSTEM_ALARM_WIFI = "Wifi";
+    private static final String SUBSYSTEM_WIFI_STRING = "Wifi";
+    private static final String SUBSYSTEM_SOUND_TRIGGER_STRING = "Sound_trigger";
     private static final String TRACE_TRACK_WAKEUP_ATTRIBUTION = "wakeup_attribution";
     @VisibleForTesting
     static final long WAKEUP_REASON_HALF_WINDOW_MS = 500;
@@ -91,12 +93,24 @@
         mConfig.register(new HandlerExecutor(mHandler));
     }
 
+    private static int typeToStatsType(int wakeupType) {
+        switch (wakeupType) {
+            case Wakeup.TYPE_ABNORMAL:
+                return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__TYPE__TYPE_ABNORMAL;
+            case Wakeup.TYPE_IRQ:
+                return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__TYPE__TYPE_IRQ;
+        }
+        return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__TYPE__TYPE_UNKNOWN;
+    }
+
     private static int subsystemToStatsReason(int subsystem) {
         switch (subsystem) {
             case CPU_WAKEUP_SUBSYSTEM_ALARM:
                 return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__ALARM;
             case CPU_WAKEUP_SUBSYSTEM_WIFI:
                 return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__WIFI;
+            case CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER:
+                return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__SOUND_TRIGGER;
         }
         return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__UNKNOWN;
     }
@@ -144,7 +158,7 @@
                 }
             }
             FrameworkStatsLog.write(FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED,
-                    FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__TYPE__TYPE_IRQ,
+                    typeToStatsType(wakeupToLog.mType),
                     subsystemToStatsReason(subsystem),
                     uids,
                     wakeupToLog.mElapsedMillis,
@@ -524,8 +538,10 @@
         switch (rawSubsystem) {
             case SUBSYSTEM_ALARM_STRING:
                 return CPU_WAKEUP_SUBSYSTEM_ALARM;
-            case SUBSYSTEM_ALARM_WIFI:
+            case SUBSYSTEM_WIFI_STRING:
                 return CPU_WAKEUP_SUBSYSTEM_WIFI;
+            case SUBSYSTEM_SOUND_TRIGGER_STRING:
+                return CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER;
         }
         return CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
     }
@@ -535,25 +551,43 @@
             case CPU_WAKEUP_SUBSYSTEM_ALARM:
                 return SUBSYSTEM_ALARM_STRING;
             case CPU_WAKEUP_SUBSYSTEM_WIFI:
-                return SUBSYSTEM_ALARM_WIFI;
+                return SUBSYSTEM_WIFI_STRING;
+            case CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER:
+                return SUBSYSTEM_SOUND_TRIGGER_STRING;
             case CPU_WAKEUP_SUBSYSTEM_UNKNOWN:
                 return "Unknown";
         }
         return "N/A";
     }
 
-    private static final class Wakeup {
+    @VisibleForTesting
+    static final class Wakeup {
         private static final String PARSER_TAG = "CpuWakeupStats.Wakeup";
         private static final String ABORT_REASON_PREFIX = "Abort";
-        private static final Pattern sIrqPattern = Pattern.compile("^(\\d+)\\s+(\\S+)");
+        private static final Pattern sIrqPattern = Pattern.compile("^(\\-?\\d+)\\s+(\\S+)");
+
+        /**
+         * Classical interrupts, which arrive on a dedicated GPIO pin into the main CPU.
+         * Sometimes, when multiple IRQs happen close to each other, they may get batched together.
+         */
+        static final int TYPE_IRQ = 1;
+
+        /**
+         * Non-IRQ wakeups. The exact mechanism for these is unknown, except that these explicitly
+         * do not use an interrupt line or a GPIO pin.
+         */
+        static final int TYPE_ABNORMAL = 2;
+
+        int mType;
         long mElapsedMillis;
         long mUptimeMillis;
         IrqDevice[] mDevices;
 
-        private Wakeup(IrqDevice[] devices, long elapsedMillis, long uptimeMillis) {
+        private Wakeup(int type, IrqDevice[] devices, long elapsedMillis, long uptimeMillis) {
+            mType = type;
+            mDevices = devices;
             mElapsedMillis = elapsedMillis;
             mUptimeMillis = uptimeMillis;
-            mDevices = devices;
         }
 
         static Wakeup parseWakeup(String rawReason, long elapsedMillis, long uptimeMillis) {
@@ -563,6 +597,7 @@
                 return null;
             }
 
+            int type = TYPE_IRQ;
             int parsedDeviceCount = 0;
             final IrqDevice[] parsedDevices = new IrqDevice[components.length];
 
@@ -574,6 +609,10 @@
                     try {
                         line = Integer.parseInt(matcher.group(1));
                         device = matcher.group(2);
+                        if (line < 0) {
+                            // Assuming that IRQ wakeups cannot come batched with non-IRQ wakeups.
+                            type = TYPE_ABNORMAL;
+                        }
                     } catch (NumberFormatException e) {
                         Slog.e(PARSER_TAG,
                                 "Exception while parsing device names from part: " + component, e);
@@ -585,15 +624,16 @@
             if (parsedDeviceCount == 0) {
                 return null;
             }
-            return new Wakeup(Arrays.copyOf(parsedDevices, parsedDeviceCount), elapsedMillis,
+            return new Wakeup(type, Arrays.copyOf(parsedDevices, parsedDeviceCount), elapsedMillis,
                     uptimeMillis);
         }
 
         @Override
         public String toString() {
             return "Wakeup{"
-                    + "mElapsedMillis=" + mElapsedMillis
-                    + ", mUptimeMillis=" + TimeUtils.formatDuration(mUptimeMillis)
+                    + "mType=" + mType
+                    + ", mElapsedMillis=" + mElapsedMillis
+                    + ", mUptimeMillis=" + mUptimeMillis
                     + ", mDevices=" + Arrays.toString(mDevices)
                     + '}';
         }
diff --git a/services/core/java/com/android/server/power/stats/IrqDeviceMap.java b/services/core/java/com/android/server/power/stats/wakeups/IrqDeviceMap.java
similarity index 98%
rename from services/core/java/com/android/server/power/stats/IrqDeviceMap.java
rename to services/core/java/com/android/server/power/stats/wakeups/IrqDeviceMap.java
index 091d18e..8644f72 100644
--- a/services/core/java/com/android/server/power/stats/IrqDeviceMap.java
+++ b/services/core/java/com/android/server/power/stats/wakeups/IrqDeviceMap.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.power.stats;
+package com.android.server.power.stats.wakeups;
 
 import android.annotation.XmlRes;
 import android.content.Context;
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 7718dd8..8bbcd27 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -97,7 +97,6 @@
 
 import com.android.internal.app.AssistUtils;
 import com.android.internal.policy.IKeyguardDismissCallback;
-import com.android.internal.protolog.ProtoLogGroup;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.server.LocalServices;
 import com.android.server.Watchdog;
@@ -1142,18 +1141,11 @@
         // Initiate the transition.
         final Transition transition = new Transition(TRANSIT_CHANGE, 0 /* flags */, controller,
                 mService.mWindowManager.mSyncEngine);
-        if (mService.mWindowManager.mSyncEngine.hasActiveSync()) {
-            ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
-                    "Creating Pending Multiwindow Fullscreen Request: %s", transition);
-            r.mTransitionController.queueCollecting(transition,
-                    () -> {
-                        executeFullscreenRequestTransition(fullscreenRequest, callback, r,
-                                transition, true /* queued */);
-                    });
-        } else {
-            executeFullscreenRequestTransition(fullscreenRequest, callback, r, transition,
-                    false /* queued */);
-        }
+        r.mTransitionController.startCollectOrQueue(transition,
+                (deferred) -> {
+                    executeFullscreenRequestTransition(fullscreenRequest, callback, r,
+                            transition, deferred);
+                });
     }
 
     private void executeFullscreenRequestTransition(int fullscreenRequest, IRemoteCallback callback,
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 0b98495..64e1ae5 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2865,11 +2865,9 @@
             return;
         }
 
-        if (animate && mTransitionController.inCollectingTransition(startingWindow)
-                && startingWindow.cancelAndRedraw()) {
+        if (animate && mTransitionController.inCollectingTransition(startingWindow)) {
             // Defer remove starting window after transition start.
-            // If splash screen window was in collecting, the client side is unable to draw because
-            // of Session#cancelDraw, which will blocking the remove animation.
+            // The surface of app window could really show after the transition finish.
             startingWindow.mSyncTransaction.addTransactionCommittedListener(Runnable::run, () -> {
                 synchronized (mAtmService.mGlobalLock) {
                     surface.remove(true);
@@ -9806,7 +9804,7 @@
         if (mTransitionController.isShellTransitionsEnabled()) {
             final Transition transition = new Transition(TRANSIT_RELAUNCH, 0 /* flags */,
                     mTransitionController, mWmService.mSyncEngine);
-            final Runnable executeRestart = () -> {
+            mTransitionController.startCollectOrQueue(transition, (deferred) -> {
                 if (mState != RESTARTING_PROCESS || !attachedToProcess()) {
                     transition.abort();
                     return;
@@ -9818,13 +9816,7 @@
                 mTransitionController.requestStartTransition(transition, task,
                         null /* remoteTransition */, null /* displayChange */);
                 scheduleStopForRestartProcess();
-            };
-            if (mWmService.mSyncEngine.hasActiveSync()) {
-                mTransitionController.queueCollecting(transition, executeRestart);
-            } else {
-                mTransitionController.moveToCollecting(transition);
-                executeRestart.run();
-            }
+            });
         } else {
             startFreezingScreen();
             scheduleStopForRestartProcess();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index e780716..064af0f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -250,7 +250,6 @@
 import com.android.internal.os.TransferPipe;
 import com.android.internal.policy.AttributeCache;
 import com.android.internal.policy.KeyguardDismissCallback;
-import com.android.internal.protolog.ProtoLogGroup;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
@@ -2873,28 +2872,19 @@
 
                 final Transition transition = new Transition(TRANSIT_CHANGE, 0 /* flags */,
                         getTransitionController(), mWindowManager.mSyncEngine);
-                if (mWindowManager.mSyncEngine.hasActiveSync()) {
-                    getTransitionController().queueCollecting(transition,
-                            () -> {
-                                if (!task.getWindowConfiguration().canResizeTask()) {
-                                    Slog.w(TAG, "resizeTask not allowed on task=" + task);
-                                    transition.abort();
-                                    return;
-                                }
-                                getTransitionController().requestStartTransition(transition, task,
-                                        null /* remoteTransition */, null /* displayChange */);
-                                getTransitionController().collect(task);
-                                task.resize(bounds, resizeMode, preserveWindow);
-                                transition.setReady(task, true);
-                            });
-                } else {
-                    getTransitionController().moveToCollecting(transition);
-                    getTransitionController().requestStartTransition(transition, task,
-                            null /* remoteTransition */, null /* displayChange */);
-                    getTransitionController().collect(task);
-                    task.resize(bounds, resizeMode, preserveWindow);
-                    transition.setReady(task, true);
-                }
+                getTransitionController().startCollectOrQueue(transition,
+                        (deferred) -> {
+                            if (deferred && !task.getWindowConfiguration().canResizeTask()) {
+                                Slog.w(TAG, "resizeTask not allowed on task=" + task);
+                                transition.abort();
+                                return;
+                            }
+                            getTransitionController().requestStartTransition(transition, task,
+                                    null /* remoteTransition */, null /* displayChange */);
+                            getTransitionController().collect(task);
+                            task.resize(bounds, resizeMode, preserveWindow);
+                            transition.setReady(task, true);
+                        });
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -3625,30 +3615,25 @@
             mActivityClientController.dismissKeyguard(r.token, new KeyguardDismissCallback() {
                 @Override
                 public void onDismissSucceeded() {
-                    if (transition != null && mWindowManager.mSyncEngine.hasActiveSync()) {
-                        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
-                                "Creating Pending Pip-Enter: %s", transition);
-                        getTransitionController().queueCollecting(transition, enterPipRunnable);
-                    } else {
-                        // Move to collecting immediately to "claim" the sync-engine for this
-                        // transition.
-                        if (transition != null) {
-                            getTransitionController().moveToCollecting(transition);
-                        }
+                    if (transition == null) {
                         mH.post(enterPipRunnable);
+                        return;
                     }
+                    getTransitionController().startCollectOrQueue(transition, (deferred) -> {
+                        if (deferred) {
+                            enterPipRunnable.run();
+                        } else {
+                            mH.post(enterPipRunnable);
+                        }
+                    });
                 }
             }, null /* message */);
         } else {
             // Enter picture in picture immediately otherwise
-            if (transition != null && mWindowManager.mSyncEngine.hasActiveSync()) {
-                ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
-                        "Creating Pending Pip-Enter: %s", transition);
-                getTransitionController().queueCollecting(transition, enterPipRunnable);
+            if (transition != null) {
+                getTransitionController().startCollectOrQueue(transition,
+                        (deferred) -> enterPipRunnable.run());
             } else {
-                if (transition != null) {
-                    getTransitionController().moveToCollecting(transition);
-                }
                 enterPipRunnable.run();
             }
         }
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index c6db8a7..8660bec 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -263,16 +263,19 @@
      * {@link WindowContainer#prepareSurfaces}. After calling this, the container should
      * chain {@link WindowContainer#prepareSurfaces} down to it's children to give them
      * a chance to request dims to continue.
-     * @return Non-null dim bounds if the dimmer is showing.
      */
-    Rect resetDimStates() {
+    void resetDimStates() {
         if (mDimState == null) {
-            return null;
+            return;
         }
         if (!mDimState.mDontReset) {
             mDimState.mDimming = false;
         }
-        return mDimState.mDimBounds;
+    }
+
+    /** Returns non-null bounds if the dimmer is showing. */
+    Rect getDimBounds() {
+        return mDimState != null ? mDimState.mDimBounds : null;
     }
 
     void dontAnimateExit() {
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 26f56a2..9f59f5a 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -779,8 +779,9 @@
 
         @Override
         void prepareSurfaces() {
-            final Rect dimBounds = mDimmer.resetDimStates();
+            mDimmer.resetDimStates();
             super.prepareSurfaces();
+            final Rect dimBounds = mDimmer.getDimBounds();
             if (dimBounds != null) {
                 // Bounds need to be relative, as the dim layer is a child.
                 getBounds(dimBounds);
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ad93454..cd4b3c5 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2328,17 +2328,23 @@
                 // this as a signal to the transition-player.
                 final Transition transition = new Transition(TRANSIT_SLEEP, 0 /* flags */,
                         display.mTransitionController, mWmService.mSyncEngine);
-                final Runnable sendSleepTransition = () -> {
+                final TransitionController.OnStartCollect sendSleepTransition = (deferred) -> {
                     display.mTransitionController.requestStartTransition(transition,
                             null /* trigger */, null /* remote */, null /* display */);
                     // Force playing immediately so that unrelated ops can't be collected.
                     transition.playNow();
                 };
-                if (display.mTransitionController.isCollecting()) {
-                    display.mTransitionController.queueCollecting(transition, sendSleepTransition);
-                } else {
+                if (!display.mTransitionController.isCollecting()) {
+                    // Since this bypasses sync, submit directly ignoring whether sync-engine
+                    // is active.
+                    if (mWindowManager.mSyncEngine.hasActiveSync()) {
+                        Slog.w(TAG, "Ongoing sync outside of a transition.");
+                    }
                     display.mTransitionController.moveToCollecting(transition);
-                    sendSleepTransition.run();
+                    sendSleepTransition.onCollectStarted(false /* deferred */);
+                } else {
+                    display.mTransitionController.startCollectOrQueue(transition,
+                            sendSleepTransition);
                 }
             }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 89f9753..9363eb5 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -188,7 +188,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.protolog.ProtoLogGroup;
 import com.android.internal.protolog.common.ProtoLog;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.util.function.pooled.PooledLambda;
@@ -3253,9 +3252,10 @@
 
     @Override
     void prepareSurfaces() {
-        final Rect dimBounds = mDimmer.resetDimStates();
+        mDimmer.resetDimStates();
         super.prepareSurfaces();
 
+        final Rect dimBounds = mDimmer.getDimBounds();
         if (dimBounds != null) {
             getDimBounds(dimBounds);
 
@@ -5632,29 +5632,20 @@
             final Transition transition = new Transition(TRANSIT_TO_BACK, 0 /* flags */,
                     mTransitionController, mWmService.mSyncEngine);
             // Guarantee that this gets its own transition by queueing on SyncEngine
-            if (mWmService.mSyncEngine.hasActiveSync()) {
-                ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
-                        "Creating Pending Move-to-back: %s", transition);
-                mTransitionController.queueCollecting(transition,
-                        () -> {
-                            // Need to check again since this happens later and the system might
-                            // be in a different state.
-                            if (!canMoveTaskToBack(tr)) {
-                                Slog.e(TAG, "Failed to move task to back after saying we could: "
-                                        + tr.mTaskId);
-                                transition.abort();
-                                return;
-                            }
-                            mTransitionController.requestStartTransition(transition, tr,
-                                    null /* remoteTransition */, null /* displayChange */);
-                            moveTaskToBackInner(tr);
-                        });
-            } else {
-                mTransitionController.moveToCollecting(transition);
-                mTransitionController.requestStartTransition(transition, tr,
-                        null /* remoteTransition */, null /* displayChange */);
-                moveTaskToBackInner(tr);
-            }
+            mTransitionController.startCollectOrQueue(transition,
+                    (deferred) -> {
+                        // Need to check again if deferred since the system might
+                        // be in a different state.
+                        if (deferred && !canMoveTaskToBack(tr)) {
+                            Slog.e(TAG, "Failed to move task to back after saying we could: "
+                                    + tr.mTaskId);
+                            transition.abort();
+                            return;
+                        }
+                        mTransitionController.requestStartTransition(transition, tr,
+                                null /* remoteTransition */, null /* displayChange */);
+                        moveTaskToBackInner(tr);
+                    });
         } else {
             // Skip the transition for pinned task.
             if (!inPinnedWindowingMode()) {
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 1d232fe..311b9a6 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -2923,9 +2923,10 @@
             return;
         }
 
-        final Rect dimBounds = mDimmer.resetDimStates();
+        mDimmer.resetDimStates();
         super.prepareSurfaces();
 
+        final Rect dimBounds = mDimmer.getDimBounds();
         if (dimBounds != null) {
             // Bounds need to be relative, as the dim layer is a child.
             dimBounds.offsetTo(0 /* newLeft */, 0 /* newTop */);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 652c297..76b0e7b 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -2835,7 +2835,7 @@
             final String name = isDisplayRotation ? "RotationLayer" : "transition snapshot: " + wc;
             SurfaceControl snapshotSurface = wc.makeAnimationLeash()
                     .setName(name)
-                    .setOpaque(true)
+                    .setOpaque(wc.fillsParent())
                     .setParent(wc.getSurfaceControl())
                     .setSecure(screenshotBuffer.containsSecureLayers())
                     .setCallsite("Transition.ScreenshotSync")
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 4c1c2ff..cbb4fe2 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -884,13 +884,30 @@
         proto.end(token);
     }
 
-    void queueCollecting(Transition transit, Runnable onCollectStart) {
-        mAtm.mWindowManager.mSyncEngine.queueSyncSet(
-                // Make sure to collect immediately to prevent another transition
-                // from sneaking in before it. Note: moveToCollecting internally
-                // calls startSyncSet.
-                () -> moveToCollecting(transit),
-                onCollectStart);
+    /** Returns {@code true} if it started collecting, {@code false} if it was queued. */
+    boolean startCollectOrQueue(Transition transit, OnStartCollect onStartCollect) {
+        if (mAtm.mWindowManager.mSyncEngine.hasActiveSync()) {
+            if (!isCollecting()) {
+                Slog.w(TAG, "Ongoing Sync outside of transition.");
+            }
+            ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN,
+                    "Queueing transition: %s", transit);
+            mAtm.mWindowManager.mSyncEngine.queueSyncSet(
+                    // Make sure to collect immediately to prevent another transition
+                    // from sneaking in before it. Note: moveToCollecting internally
+                    // calls startSyncSet.
+                    () -> moveToCollecting(transit),
+                    () -> onStartCollect.onCollectStarted(true /* deferred */));
+            return false;
+        } else {
+            moveToCollecting(transit);
+            onStartCollect.onCollectStarted(false /* deferred */);
+            return true;
+        }
+    }
+
+    interface OnStartCollect {
+        void onCollectStarted(boolean deferred);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index f63470f2..ee86b97 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -280,41 +280,31 @@
                     applyTransaction(t, -1 /* syncId */, null, caller);
                     return null;
                 }
-                // In cases where transition is already provided, the "readiness lifecycle" of the
-                // transition is determined outside of this transaction. However, if this is a
-                // direct call from shell, the entire transition lifecycle is contained in the
-                // provided transaction and thus we can setReady immediately after apply.
-                final boolean needsSetReady = transition == null && t != null;
                 final WindowContainerTransaction wct =
                         t != null ? t : new WindowContainerTransaction();
                 if (transition == null) {
                     if (type < 0) {
                         throw new IllegalArgumentException("Can't create transition with no type");
                     }
-                    transition = new Transition(type, 0 /* flags */, mTransitionController,
-                            mService.mWindowManager.mSyncEngine);
-                    // If there is already a collecting transition, queue up a new transition and
-                    // return that. The actual start and apply will then be deferred until that
-                    // transition starts collecting. This should almost never happen except during
-                    // tests.
-                    if (mService.mWindowManager.mSyncEngine.hasActiveSync()) {
-                        Slog.w(TAG, "startTransition() while one is already collecting.");
-                        final Transition nextTransition = transition;
-                        ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
-                                "Creating Pending Transition: %s", nextTransition);
-                        mTransitionController.queueCollecting(nextTransition,
-                                () -> {
-                                    nextTransition.start();
-                                    nextTransition.mLogger.mStartWCT = wct;
-                                    applyTransaction(wct, -1 /*syncId*/, nextTransition, caller);
-                                    if (needsSetReady) {
-                                        nextTransition.setAllReady();
-                                    }
-                                });
-                        return nextTransition.getToken();
-                    }
-                    mTransitionController.moveToCollecting(transition);
+                    // This is a direct call from shell, so the entire transition lifecycle is
+                    // contained in the provided transaction if provided. Thus, we can setReady
+                    // immediately after apply.
+                    final boolean needsSetReady = t != null;
+                    final Transition nextTransition = new Transition(type, 0 /* flags */,
+                            mTransitionController, mService.mWindowManager.mSyncEngine);
+                    mTransitionController.startCollectOrQueue(nextTransition,
+                            (deferred) -> {
+                                nextTransition.start();
+                                nextTransition.mLogger.mStartWCT = wct;
+                                applyTransaction(wct, -1 /*syncId*/, nextTransition, caller);
+                                if (needsSetReady) {
+                                    nextTransition.setAllReady();
+                                }
+                            });
+                    return nextTransition.getToken();
                 }
+                // The transition already started collecting before sending a request to shell,
+                // so just start here.
                 if (!transition.isCollecting() && !transition.isForcePlaying()) {
                     Slog.e(TAG, "Trying to start a transition that isn't collecting. This probably"
                             + " means Shell took too long to respond to a request. WM State may be"
@@ -325,9 +315,8 @@
                 transition.start();
                 transition.mLogger.mStartWCT = wct;
                 applyTransaction(wct, -1 /*syncId*/, transition, caller);
-                if (needsSetReady) {
-                    transition.setAllReady();
-                }
+                // Since the transition is already provided, it means WMCore is determining the
+                // "readiness lifecycle" outside the provided transaction, so don't set ready here.
                 return transition.getToken();
             }
         } finally {
@@ -432,23 +421,8 @@
                 return;
             }
 
-            if (!mService.mWindowManager.mSyncEngine.hasActiveSync()) {
-                // Sync is for either transition or applySyncTransaction(). We don't support
-                // multiple sync at the same time because it may cause conflict.
-                // Create a new transition when there is no active sync to collect the changes.
-                final Transition transition = mTransitionController.createTransition(type);
-                if (applyTransaction(wct, -1 /* syncId */, transition, caller)
-                        == TRANSACT_EFFECTS_NONE && transition.mParticipants.isEmpty()) {
-                    transition.abort();
-                    return;
-                }
-                mTransitionController.requestStartTransition(transition, null /* startTask */,
-                        null /* remoteTransition */, null /* displayChange */);
-                transition.setAllReady();
-                return;
-            }
-
-            if (!shouldApplyIndependently) {
+            if (mService.mWindowManager.mSyncEngine.hasActiveSync()
+                    && !shouldApplyIndependently) {
                 // Although there is an active sync, we want to apply the transaction now.
                 // TODO(b/232042367) Redesign the organizer update on activity callback so that we
                 // we will know about the transition explicitly.
@@ -467,25 +441,23 @@
                 return;
             }
 
-            // It is ok to queue the WCT until the sync engine is free.
-            final Transition nextTransition = new Transition(type, 0 /* flags */,
+            final Transition transition = new Transition(type, 0 /* flags */,
                     mTransitionController, mService.mWindowManager.mSyncEngine);
-            ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
-                    "Creating Pending Transition for TaskFragment: %s", nextTransition);
-            mTransitionController.queueCollecting(nextTransition,
-                    () -> {
-                        if (mTaskFragmentOrganizerController.isValidTransaction(wct)
-                                && (applyTransaction(wct, -1 /* syncId */, nextTransition, caller)
-                                        != TRANSACT_EFFECTS_NONE
-                                || !nextTransition.mParticipants.isEmpty())) {
-                            mTransitionController.requestStartTransition(nextTransition,
-                                    null /* startTask */, null /* remoteTransition */,
-                                    null /* displayChange */);
-                            nextTransition.setAllReady();
-                            return;
-                        }
-                        nextTransition.abort();
-                    });
+            TransitionController.OnStartCollect doApply = (deferred) -> {
+                if (deferred && !mTaskFragmentOrganizerController.isValidTransaction(wct)) {
+                    transition.abort();
+                    return;
+                }
+                if (applyTransaction(wct, -1 /* syncId */, transition, caller)
+                        == TRANSACT_EFFECTS_NONE && transition.mParticipants.isEmpty()) {
+                    transition.abort();
+                    return;
+                }
+                mTransitionController.requestStartTransition(transition, null /* startTask */,
+                        null /* remoteTransition */, null /* displayChange */);
+                transition.setAllReady();
+            };
+            mTransitionController.startCollectOrQueue(transition, doApply);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index a04143a..5a71808 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -89,14 +89,16 @@
     protected void launchUiWithProviderData(ArrayList<ProviderData> providerDataList) {
         mRequestSessionMetric.collectUiCallStartTime(System.nanoTime());
         mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.USER_INTERACTION);
+        cancelExistingPendingIntent();
         try {
-            mClientCallback.onPendingIntent(mCredentialManagerUi.createPendingIntent(
+            mPendingIntent = mCredentialManagerUi.createPendingIntent(
                     RequestInfo.newCreateRequestInfo(
                             mRequestId, mClientRequest,
                             mClientAppInfo.getPackageName(),
                             PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
                                     Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)),
-                    providerDataList));
+                    providerDataList);
+            mClientCallback.onPendingIntent(mPendingIntent);
         } catch (RemoteException e) {
             mRequestSessionMetric.collectUiReturnedFinalPhase(/*uiReturned=*/ false);
             mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.TERMINATED);
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index 8750906..0dee7a4 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -33,7 +33,6 @@
 import android.os.Looper;
 import android.os.ResultReceiver;
 import android.service.credentials.CredentialProviderInfoFactory;
-import android.util.Log;
 import android.util.Slog;
 
 import java.util.ArrayList;
@@ -124,7 +123,6 @@
 
     public CredentialManagerUi(Context context, int userId,
             CredentialManagerUiCallback callbacks, Set<ComponentName> enabledProviders) {
-        Log.i(TAG, "In CredentialManagerUi constructor");
         mContext = context;
         mUserId = userId;
         mCallbacks = callbacks;
@@ -151,8 +149,6 @@
      */
     public PendingIntent createPendingIntent(
             RequestInfo requestInfo, ArrayList<ProviderData> providerDataList) {
-        Log.i(TAG, "In createPendingIntent");
-
         List<CredentialProviderInfo> allProviders =
                 CredentialProviderInfoFactory.getCredentialProviderServices(
                         mContext,
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index 208a4be..3dba4a9 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -89,11 +89,13 @@
     protected void launchUiWithProviderData(ArrayList<ProviderData> providerDataList) {
         mRequestSessionMetric.collectUiCallStartTime(System.nanoTime());
         mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.USER_INTERACTION);
+        cancelExistingPendingIntent();
         try {
-            mClientCallback.onPendingIntent(mCredentialManagerUi.createPendingIntent(
+            mPendingIntent = mCredentialManagerUi.createPendingIntent(
                     RequestInfo.newGetRequestInfo(
                             mRequestId, mClientRequest, mClientAppInfo.getPackageName()),
-                    providerDataList));
+                    providerDataList);
+            mClientCallback.onPendingIntent(mPendingIntent);
         } catch (RemoteException e) {
             mRequestSessionMetric.collectUiReturnedFinalPhase(/*uiReturned=*/ false);
             mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.TERMINATED);
diff --git a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
index c1e9bc6..0ad73c9 100644
--- a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
+++ b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
@@ -40,7 +40,6 @@
 import android.service.credentials.IClearCredentialStateCallback;
 import android.service.credentials.ICredentialProviderService;
 import android.text.format.DateUtils;
-import android.util.Log;
 import android.util.Slog;
 
 import com.android.internal.infra.ServiceConnector;
@@ -122,7 +121,6 @@
      */
     public void onBeginGetCredential(@NonNull BeginGetCredentialRequest request,
             ProviderCallbacks<BeginGetCredentialResponse> callback) {
-        Log.i(TAG, "In onGetCredentials in RemoteCredentialService");
         AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
         AtomicReference<CompletableFuture<BeginGetCredentialResponse>> futureRef =
                 new AtomicReference<>();
@@ -142,7 +140,6 @@
 
                             @Override
                             public void onFailure(String errorType, CharSequence message) {
-                                Log.i(TAG, "In onFailure in RemoteCredentialService");
                                 String errorMsg = message == null ? "" : String.valueOf(
                                         message);
                                 getCredentials.completeExceptionally(
@@ -182,7 +179,6 @@
      */
     public void onBeginCreateCredential(@NonNull BeginCreateCredentialRequest request,
             ProviderCallbacks<BeginCreateCredentialResponse> callback) {
-        Log.i(TAG, "In onCreateCredential in RemoteCredentialService");
         AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
         AtomicReference<CompletableFuture<BeginCreateCredentialResponse>> futureRef =
                 new AtomicReference<>();
@@ -197,14 +193,11 @@
                                 request, new IBeginCreateCredentialCallback.Stub() {
                                     @Override
                                     public void onSuccess(BeginCreateCredentialResponse response) {
-                                        Log.i(TAG, "In onSuccess onBeginCreateCredential "
-                                                + "in RemoteCredentialService");
                                         createCredentialFuture.complete(response);
                                     }
 
                                     @Override
                                     public void onFailure(String errorType, CharSequence message) {
-                                        Log.i(TAG, "In onFailure in RemoteCredentialService");
                                         String errorMsg = message == null ? "" : String.valueOf(
                                                 message);
                                         createCredentialFuture.completeExceptionally(
@@ -244,7 +237,6 @@
      */
     public void onClearCredentialState(@NonNull ClearCredentialStateRequest request,
             ProviderCallbacks<Void> callback) {
-        Log.i(TAG, "In onClearCredentialState in RemoteCredentialService");
         AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
         AtomicReference<CompletableFuture<Void>> futureRef = new AtomicReference<>();
 
@@ -258,14 +250,11 @@
                                 request, new IClearCredentialStateCallback.Stub() {
                                     @Override
                                     public void onSuccess() {
-                                        Log.i(TAG, "In onSuccess onClearCredentialState "
-                                                + "in RemoteCredentialService");
                                         clearCredentialFuture.complete(null);
                                     }
 
                                     @Override
                                     public void onFailure(String errorType, CharSequence message) {
-                                        Log.i(TAG, "In onFailure in RemoteCredentialService");
                                         String errorMsg = message == null ? "" :
                                                 String.valueOf(message);
                                         clearCredentialFuture.completeExceptionally(
@@ -300,35 +289,29 @@
             AtomicReference<ICancellationSignal> cancellationSink,
             ProviderCallbacks<T> callback) {
         if (error == null) {
-            Log.i(TAG, "In RemoteCredentialService execute error is null");
             callback.onProviderResponseSuccess(result);
         } else {
             if (error instanceof TimeoutException) {
-                Log.i(TAG, "In RemoteCredentialService execute error is timeout");
+                Slog.d(TAG, "Remote provider response timed tuo for: " + mComponentName);
                 dispatchCancellationSignal(cancellationSink.get());
                 callback.onProviderResponseFailure(
                         CredentialProviderErrors.ERROR_TIMEOUT,
                         null);
             } else if (error instanceof CancellationException) {
-                Log.i(TAG, "In RemoteCredentialService execute error is cancellation");
+                Slog.d(TAG, "Cancellation exception for remote provider: " + mComponentName);
                 dispatchCancellationSignal(cancellationSink.get());
                 callback.onProviderResponseFailure(
                         CredentialProviderErrors.ERROR_TASK_CANCELED,
                         null);
             } else if (error instanceof GetCredentialException) {
-                Log.i(TAG, "In RemoteCredentialService execute error is provider get"
-                        + "error");
                 callback.onProviderResponseFailure(
                         CredentialProviderErrors.ERROR_PROVIDER_FAILURE,
                         (GetCredentialException) error);
             } else if (error instanceof CreateCredentialException) {
-                Log.i(TAG, "In RemoteCredentialService execute error is provider create "
-                        + "error");
                 callback.onProviderResponseFailure(
                         CredentialProviderErrors.ERROR_PROVIDER_FAILURE,
                         (CreateCredentialException) error);
             } else {
-                Log.i(TAG, "In RemoteCredentialService execute error is unknown");
                 callback.onProviderResponseFailure(
                         CredentialProviderErrors.ERROR_UNKNOWN,
                         (Exception) error);
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index 8fd0269..15a30e4 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
+import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -90,6 +91,8 @@
 
     private final Set<ComponentName> mEnabledProviders;
 
+    protected PendingIntent mPendingIntent;
+
     @NonNull
     protected RequestSessionStatus mRequestSessionStatus =
             RequestSessionStatus.IN_PROGRESS;
@@ -202,11 +205,23 @@
         if (propagateCancellation) {
             mProviders.values().forEach(ProviderSession::cancelProviderRemoteSession);
         }
+        cancelExistingPendingIntent();
         mRequestSessionStatus = RequestSessionStatus.COMPLETE;
         mProviders.clear();
         clearRequestSessionLocked();
     }
 
+    void cancelExistingPendingIntent() {
+        if (mPendingIntent != null) {
+            try {
+                mPendingIntent.cancel();
+                mPendingIntent = null;
+            } catch (Exception e) {
+                Slog.e(TAG, "Unable to cancel existing pending intent", e);
+            }
+        }
+    }
+
     private void clearRequestSessionLocked() {
         synchronized (mLock) {
             mSessionCallback.onFinishRequestSession(mUserId, mRequestId);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 926c7e4..3d5686d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -910,7 +910,7 @@
             mDeviceAdminServiceController.stopServicesForUser(
                     userId, actionForLog);
         } else {
-            for (EnforcingAdmin admin : getEnforcingAdminsForUser(userId)) {
+            for (EnforcingAdmin admin : getEnforcingAdminsOnUser(userId)) {
                 // DPCs are handled separately in DPMS, no need to reestablish the connection here.
                 if (admin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) {
                     continue;
@@ -921,6 +921,51 @@
         }
     }
 
+    /**
+     * Handles internal state related to a user getting started.
+     */
+    void handleStartUser(int userId) {
+        updateDeviceAdminsServicesForUser(
+                userId, /* enable= */ true, /* actionForLog= */ "start-user");
+    }
+
+    /**
+     * Handles internal state related to a user getting started.
+     */
+    void handleUnlockUser(int userId) {
+        updateDeviceAdminsServicesForUser(
+                userId, /* enable= */ true, /* actionForLog= */ "unlock-user");
+    }
+
+    /**
+     * Handles internal state related to a user getting stopped.
+     */
+    void handleStopUser(int userId) {
+        updateDeviceAdminsServicesForUser(
+                userId, /* enable= */ false, /* actionForLog= */ "stop-user");
+    }
+
+    /**
+     * Handles internal state related to packages getting updated.
+     */
+    void handlePackageChanged(@Nullable String updatedPackage, int userId) {
+        if (updatedPackage == null) {
+            return;
+        }
+        updateDeviceAdminServiceOnPackageChanged(updatedPackage, userId);
+    }
+
+    /**
+     * Handles internal state related to a user getting removed.
+     */
+    void handleUserRemoved(int userId) {
+        removeLocalPoliciesForUser(userId);
+        removePoliciesForAdminsOnUser(userId);
+    }
+
+    /**
+     * Handles internal state related to a user getting created.
+     */
     void handleUserCreated(UserInfo user) {
         enforcePoliciesOnInheritableProfilesIfApplicable(user);
     }
@@ -963,40 +1008,6 @@
     }
 
     /**
-     * Handles internal state related to a user getting started.
-     */
-    void handleStartUser(int userId) {
-        updateDeviceAdminsServicesForUser(
-                userId, /* enable= */ true, /* actionForLog= */ "start-user");
-    }
-
-    /**
-     * Handles internal state related to a user getting started.
-     */
-    void handleUnlockUser(int userId) {
-        updateDeviceAdminsServicesForUser(
-                userId, /* enable= */ true, /* actionForLog= */ "unlock-user");
-    }
-
-    /**
-     * Handles internal state related to a user getting stopped.
-     */
-    void handleStopUser(int userId) {
-        updateDeviceAdminsServicesForUser(
-                userId, /* enable= */ false, /* actionForLog= */ "stop-user");
-    }
-
-    /**
-     * Handles internal state related to packages getting updated.
-     */
-    void handlePackageChanged(@Nullable String updatedPackage, int userId) {
-        if (updatedPackage == null) {
-            return;
-        }
-        updateDeviceAdminServiceOnPackageChanged(updatedPackage, userId);
-    }
-
-    /**
      * Returns all current enforced policies set on the device, and the individual values set by
      * each admin. Global policies are returned under {@link UserHandle#ALL}.
      */
@@ -1024,6 +1035,63 @@
         return new DevicePolicyState(policies);
     }
 
+
+    /**
+     * Removes all local and global policies set by that admin.
+     */
+    void removePoliciesForAdmin(EnforcingAdmin admin) {
+        Set<PolicyKey> globalPolicies = new HashSet<>(mGlobalPolicies.keySet());
+        for (PolicyKey policy : globalPolicies) {
+            PolicyState<?> policyState = mGlobalPolicies.get(policy);
+            if (policyState.getPoliciesSetByAdmins().containsKey(admin)) {
+                removeGlobalPolicy(policyState.getPolicyDefinition(), admin);
+            }
+        }
+
+        for (int i = 0; i < mLocalPolicies.size(); i++) {
+            Set<PolicyKey> localPolicies = new HashSet<>(
+                    mLocalPolicies.get(mLocalPolicies.keyAt(i)).keySet());
+            for (PolicyKey policy : localPolicies) {
+                PolicyState<?> policyState = mLocalPolicies.get(
+                        mLocalPolicies.keyAt(i)).get(policy);
+                if (policyState.getPoliciesSetByAdmins().containsKey(admin)) {
+                    removeLocalPolicy(
+                            policyState.getPolicyDefinition(), admin, mLocalPolicies.keyAt(i));
+                }
+            }
+        }
+    }
+
+    /**
+     * Removes all local policies for the provided {@code userId}.
+     */
+    private void removeLocalPoliciesForUser(int userId) {
+        Set<PolicyKey> localPolicies = new HashSet<>(mLocalPolicies.get(userId).keySet());
+        for (PolicyKey policy : localPolicies) {
+            PolicyState<?> policyState = mLocalPolicies.get(userId).get(policy);
+            Set<EnforcingAdmin> admins = new HashSet<>(
+                    policyState.getPoliciesSetByAdmins().keySet());
+            for (EnforcingAdmin admin : admins) {
+                removeLocalPolicy(
+                        policyState.getPolicyDefinition(), admin, userId);
+            }
+        }
+
+        mLocalPolicies.remove(userId);
+    }
+
+    /**
+     * Removes all local and global policies for admins installed in the provided
+     * {@code userId}.
+     */
+    private void removePoliciesForAdminsOnUser(int userId) {
+        Set<EnforcingAdmin> admins = getEnforcingAdminsOnUser(userId);
+
+        for (EnforcingAdmin admin : admins) {
+            removePoliciesForAdmin(admin);
+        }
+    }
+
     /**
      * Reestablishes the service that handles
      * {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE} in the enforcing admin if the package
@@ -1031,7 +1099,7 @@
      */
     private void updateDeviceAdminServiceOnPackageChanged(
             @NonNull String updatedPackage, int userId) {
-        for (EnforcingAdmin admin : getEnforcingAdminsForUser(userId)) {
+        for (EnforcingAdmin admin : getEnforcingAdminsOnUser(userId)) {
             // DPCs are handled separately in DPMS, no need to reestablish the connection here.
             if (admin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) {
                 continue;
@@ -1120,7 +1188,7 @@
     }
 
     @NonNull
-    private Set<EnforcingAdmin> getEnforcingAdminsForUser(int userId) {
+    private Set<EnforcingAdmin> getEnforcingAdminsOnUser(int userId) {
         return mEnforcingAdmins.contains(userId)
                 ? mEnforcingAdmins.get(userId) : Collections.emptySet();
     }
@@ -1159,12 +1227,6 @@
         }
     }
 
-    // TODO: we need to listen for user removal and package removal and update out internal policy
-    //  map and enforcing admins for this is be accurate.
-    boolean hasActivePolicies() {
-        return mEnforcingAdmins.size() > 0;
-    }
-
     private <V> boolean checkFor2gFailure(@NonNull PolicyDefinition<V> policyDefinition,
             @NonNull EnforcingAdmin enforcingAdmin) {
         if (!policyDefinition.getPolicyKey().getIdentifier().equals(
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 29f9a30..c2766c2 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -856,7 +856,7 @@
 
     private static final String ENABLE_DEVICE_POLICY_ENGINE_FOR_FINANCE_FLAG =
             "enable_device_policy_engine";
-    private static final boolean DEFAULT_ENABLE_DEVICE_POLICY_ENGINE_FOR_FINANCE_FLAG = false;
+    private static final boolean DEFAULT_ENABLE_DEVICE_POLICY_ENGINE_FOR_FINANCE_FLAG = true;
 
     // TODO(b/265683382) remove the flag after rollout.
     private static final String KEEP_PROFILES_RUNNING_FLAG = "enable_keep_profiles_running";
@@ -1178,6 +1178,9 @@
                         // Resume logging if all remaining users are affiliated.
                         maybeResumeDeviceWideLoggingLocked();
                     }
+                    if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
+                        mDevicePolicyEngine.handleUserRemoved(userHandle);
+                    }
                 }
             } else if (Intent.ACTION_USER_STARTED.equals(action)) {
                 sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_STARTED, userHandle);
@@ -3664,6 +3667,9 @@
         }
         for (Integer userId : deletedUsers) {
             removeUserData(userId);
+            if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
+                mDevicePolicyEngine.handleUserRemoved(userId);
+            }
         }
     }
 
@@ -4153,6 +4159,11 @@
 
             mInjector.binderWithCleanCallingIdentity(() ->
                     removeActiveAdminLocked(adminReceiver, userHandle));
+            if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
+                mDevicePolicyEngine.removePoliciesForAdmin(
+                        EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                                adminReceiver, userHandle, admin));
+            }
         }
     }
 
@@ -9992,6 +10003,12 @@
         toggleBackupServiceActive(UserHandle.USER_SYSTEM, true);
         pushUserControlDisabledPackagesLocked(userId);
         setGlobalSettingDeviceOwnerType(DEVICE_OWNER_TYPE_DEFAULT);
+
+        if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
+            mDevicePolicyEngine.removePoliciesForAdmin(
+                    EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                            admin.info.getComponent(), userId, admin));
+        }
     }
 
     private void clearApplicationRestrictions(int userId) {
@@ -10139,6 +10156,12 @@
         toggleBackupServiceActive(userId, true);
         applyProfileRestrictionsIfDeviceOwnerLocked();
         setNetworkLoggingActiveInternal(false);
+
+        if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
+            mDevicePolicyEngine.removePoliciesForAdmin(
+                    EnforcingAdmin.createEnterpriseEnforcingAdmin(
+                            admin.info.getComponent(), userId, admin));
+        }
     }
 
     @Override
@@ -13264,6 +13287,9 @@
                             ? getProfileParentId(caller.getUserId()) : caller.getUserId();
                     setLocalUserRestrictionInternal(admin, key, enabled, affectedUserId);
                 }
+            } else {
+                throw new IllegalStateException("Non-DO/Non-PO cannot set restriction " + key
+                        + " while targetSdkVersion is less than UPSIDE_DOWN_CAKE");
             }
         }
     }
@@ -13292,14 +13318,13 @@
                 /* who= */ null,
                 key,
                 caller.getPackageName(),
-                caller.getUserId()
+                UserHandle.USER_ALL
         );
 
         setGlobalUserRestrictionInternal(admin, key, /* enabled= */ true);
 
         logUserRestrictionCall(key, /* enabled= */ true, /* parent= */ false, caller);
     }
-
     private void setLocalUserRestrictionInternal(
             EnforcingAdmin admin, String key, boolean enabled, int userId) {
         PolicyDefinition<Boolean> policyDefinition =
@@ -13317,7 +13342,6 @@
                     userId);
         }
     }
-
     private void setGlobalUserRestrictionInternal(
             EnforcingAdmin admin, String key, boolean enabled) {
         PolicyDefinition<Boolean> policyDefinition =
@@ -16088,6 +16112,7 @@
         } else {
             long ident = mInjector.binderClearCallingIdentity();
             try {
+                // TODO(b/277908283): check in the policy engine instead of calling user manager.
                 List<UserManager.EnforcingUser> sources = mUserManager
                         .getUserRestrictionSources(restriction, UserHandle.of(userId));
                 if (sources == null) {
@@ -16119,27 +16144,14 @@
                 }
                 final UserManager.EnforcingUser enforcingUser = sources.get(0);
                 final int sourceType = enforcingUser.getUserRestrictionSource();
-                final int enforcingUserId = enforcingUser.getUserHandle().getIdentifier();
-                if (sourceType == UserManager.RESTRICTION_SOURCE_PROFILE_OWNER) {
-                    // Restriction was enforced by PO
-                    final ComponentName profileOwner = mOwners.getProfileOwnerComponent(
-                            enforcingUserId);
-                    if (profileOwner != null) {
+                if (sourceType == UserManager.RESTRICTION_SOURCE_PROFILE_OWNER
+                        || sourceType == UserManager.RESTRICTION_SOURCE_DEVICE_OWNER ) {
+                    ActiveAdmin admin = getMostProbableDPCAdminForLocalPolicy(userId);
+                    if (admin != null) {
                         result = new Bundle();
-                        result.putInt(Intent.EXTRA_USER_ID, enforcingUserId);
+                        result.putInt(Intent.EXTRA_USER_ID, admin.getUserHandle().getIdentifier());
                         result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
-                                profileOwner);
-                        return result;
-                    }
-                } else if (sourceType == UserManager.RESTRICTION_SOURCE_DEVICE_OWNER) {
-                    // Restriction was enforced by DO
-                    final Pair<Integer, ComponentName> deviceOwner =
-                            mOwners.getDeviceOwnerUserIdAndComponent();
-                    if (deviceOwner != null) {
-                        result = new Bundle();
-                        result.putInt(Intent.EXTRA_USER_ID, deviceOwner.first);
-                        result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
-                                deviceOwner.second);
+                                admin.info.getComponent());
                         return result;
                     }
                 } else if (sourceType == UserManager.RESTRICTION_SOURCE_SYSTEM) {
@@ -22549,53 +22561,53 @@
     );
 
     /**
-     * All the permisisons granted to a profile owner.
+     * All the permissions granted to a profile owner.
      */
     private static final List<String> PROFILE_OWNER_PERMISSIONS  =
             List.of(
-                MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
-                MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL,
-                MANAGE_DEVICE_POLICY_APPS_CONTROL,
-                MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
-                MANAGE_DEVICE_POLICY_AUDIO_OUTPUT,
-                MANAGE_DEVICE_POLICY_AUTOFILL,
-                MANAGE_DEVICE_POLICY_CALLS,
-                MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES,
-                MANAGE_DEVICE_POLICY_DISPLAY,
-                MANAGE_DEVICE_POLICY_FACTORY_RESET,
-                MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES,
-                MANAGE_DEVICE_POLICY_KEYGUARD,
-                MANAGE_DEVICE_POLICY_LOCALE,
-                MANAGE_DEVICE_POLICY_LOCATION,
-                MANAGE_DEVICE_POLICY_LOCK,
-                MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
-                MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION,
-                MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
-                MANAGE_DEVICE_POLICY_PACKAGE_STATE,
-                MANAGE_DEVICE_POLICY_PRINTING,
-                MANAGE_DEVICE_POLICY_PROFILES,
-                MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
-                MANAGE_DEVICE_POLICY_RESET_PASSWORD,
-                MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
-                MANAGE_DEVICE_POLICY_SCREEN_CAPTURE,
-                MANAGE_DEVICE_POLICY_SCREEN_CONTENT,
-                MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
-                MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS,
-                MANAGE_DEVICE_POLICY_TIME,
-                MANAGE_DEVICE_POLICY_VPN,
-                MANAGE_DEVICE_POLICY_WIPE_DATA
+                    MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
+                    MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL,
+                    MANAGE_DEVICE_POLICY_APPS_CONTROL,
+                    MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
+                    MANAGE_DEVICE_POLICY_AUDIO_OUTPUT,
+                    MANAGE_DEVICE_POLICY_AUTOFILL,
+                    MANAGE_DEVICE_POLICY_BLUETOOTH,
+                    MANAGE_DEVICE_POLICY_CALLS,
+                    MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES,
+                    MANAGE_DEVICE_POLICY_DISPLAY,
+                    MANAGE_DEVICE_POLICY_FACTORY_RESET,
+                    MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES,
+                    MANAGE_DEVICE_POLICY_KEYGUARD,
+                    MANAGE_DEVICE_POLICY_LOCALE,
+                    MANAGE_DEVICE_POLICY_LOCATION,
+                    MANAGE_DEVICE_POLICY_LOCK,
+                    MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
+                    MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION,
+                    MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
+                    MANAGE_DEVICE_POLICY_PACKAGE_STATE,
+                    MANAGE_DEVICE_POLICY_PRINTING,
+                    MANAGE_DEVICE_POLICY_PROFILES,
+                    MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
+                    MANAGE_DEVICE_POLICY_RESET_PASSWORD,
+                    MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
+                    MANAGE_DEVICE_POLICY_SCREEN_CAPTURE,
+                    MANAGE_DEVICE_POLICY_SCREEN_CONTENT,
+                    MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
+                    MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS,
+                    MANAGE_DEVICE_POLICY_TIME,
+                    MANAGE_DEVICE_POLICY_VPN,
+                    MANAGE_DEVICE_POLICY_WIPE_DATA
             );
 
     /**
-    * All the additional permissions granted to an organisation owned profile owner.
-    */
+     * All the additional permissions granted to an organisation owned profile owner.
+     */
     private static final List<String>
             ADDITIONAL_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS =
-                List.of(
+            List.of(
                     MANAGE_DEVICE_POLICY_ACROSS_USERS,
                     MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
                     MANAGE_DEVICE_POLICY_APPS_CONTROL,
-                    MANAGE_DEVICE_POLICY_BLUETOOTH,
                     MANAGE_DEVICE_POLICY_CAMERA,
                     MANAGE_DEVICE_POLICY_CERTIFICATES,
                     MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE,
@@ -22616,13 +22628,12 @@
                     MANAGE_DEVICE_POLICY_WIFI,
                     SET_TIME,
                     SET_TIME_ZONE
-                );
+            );
 
 
     private static final List<String> ADDITIONAL_PROFILE_OWNER_ON_USER_0_PERMISSIONS =
             List.of(
                     MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
-                    MANAGE_DEVICE_POLICY_BLUETOOTH,
                     MANAGE_DEVICE_POLICY_CAMERA,
                     MANAGE_DEVICE_POLICY_DISPLAY,
                     MANAGE_DEVICE_POLICY_FUN,
diff --git a/services/tests/PackageManagerServiceTests/TEST_MAPPING b/services/tests/PackageManagerServiceTests/TEST_MAPPING
index e98acb2..5d96af9 100644
--- a/services/tests/PackageManagerServiceTests/TEST_MAPPING
+++ b/services/tests/PackageManagerServiceTests/TEST_MAPPING
@@ -55,23 +55,10 @@
           // TODO(b/204133664)
           "exclude-filter": "com.android.server.pm.test.SdCardEjectionTests"
         },
-	{
-          // TODO(b/272575212)
-          "exclude-filter": "com.android.server.pm.test.SettingsTest#testWriteCorruptDataBinaryXml"
-	},
-	{
-          "exclude-filter": "com.android.server.pm.test.SettingsTest#testWriteCorruptDataTextXml"
-	},
-	{
-          "exclude-filter": "com.android.server.pm.test.SettingsTest#testWriteCorruptHeaderBinaryXml"
-	},
-	{
-          "exclude-filter": "com.android.server.pm.test.SettingsTest#testWriteCorruptHeaderTextXml"
-	},
-	{
+        {
           // TODO(b/272714903)
           "exclude-filter": "com.android.server.pm.test.OverlayPathsUninstallSystemUpdatesTest#verify"
-	}
+        }
       ]
     }
   ],
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 bc3f5ab..3fb7fb4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -74,16 +74,17 @@
 import android.os.DropBoxManager;
 import android.os.HandlerThread;
 import android.os.SystemClock;
+import android.os.TestLooperManager;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.IndentingPrintWriter;
 import android.util.Pair;
 
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.ExtendedMockitoRule;
-import com.android.server.am.BroadcastQueueTest.SyncBarrier;
 
 import org.junit.After;
 import org.junit.Before;
@@ -96,6 +97,7 @@
 import java.lang.reflect.Array;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 @SmallTest
 public final class BroadcastQueueModernImplTest {
@@ -111,6 +113,7 @@
     @Mock BroadcastProcessQueue mQueue4;
 
     HandlerThread mHandlerThread;
+    TestLooperManager mLooper;
 
     BroadcastConstants mConstants;
     BroadcastQueueModernImpl mImpl;
@@ -127,6 +130,10 @@
         mHandlerThread = new HandlerThread(getClass().getSimpleName());
         mHandlerThread.start();
 
+        // Pause all event processing until a test chooses to resume
+        mLooper = Objects.requireNonNull(InstrumentationRegistry.getInstrumentation()
+                .acquireLooperManager(mHandlerThread.getLooper()));
+
         mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
         mConstants.DELAY_URGENT_MILLIS = -120_000;
         mConstants.DELAY_NORMAL_MILLIS = 10_000;
@@ -167,6 +174,17 @@
         mHandlerThread.quit();
     }
 
+    /**
+     * Un-pause our handler to process pending events, wait for our queue to go
+     * idle, and then re-pause the handler.
+     */
+    private void waitForIdle() throws Exception {
+        mLooper.release();
+        mImpl.waitForIdle(LOG_WRITER_INFO);
+        mLooper = Objects.requireNonNull(InstrumentationRegistry.getInstrumentation()
+                .acquireLooperManager(mHandlerThread.getLooper()));
+    }
+
     private static void assertOrphan(BroadcastProcessQueue queue) {
         assertNull(queue.runnableAtNext);
         assertNull(queue.runnableAtPrev);
@@ -836,9 +854,6 @@
         optionsAlarmVolumeChanged.setDeliveryGroupMatchingKey("audio",
                 String.valueOf(AudioManager.STREAM_ALARM));
 
-        // Halt all processing so that we get a consistent view
-        mHandlerThread.getLooper().getQueue().postSyncBarrier();
-
         mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
         mImpl.enqueueBroadcastLocked(makeBroadcastRecord(musicVolumeChanged,
                 optionsMusicVolumeChanged));
@@ -905,9 +920,6 @@
                 String.valueOf(TEST_UID2));
         optionsPackageChangedForUid.setDeliveryGroupExtrasMerger(extrasMerger);
 
-        // Halt all processing so that we get a consistent view
-        mHandlerThread.getLooper().getQueue().postSyncBarrier();
-
         mImpl.enqueueBroadcastLocked(makeBroadcastRecord(packageChangedForUid,
                 optionsPackageChangedForUid));
         mImpl.enqueueBroadcastLocked(makeBroadcastRecord(packageChangedForUid2,
@@ -955,9 +967,6 @@
                 BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT);
         optionsAlarmVolumeChanged.setDeliveryGroupMatchingFilter(filterAlarmVolumeChanged);
 
-        // Halt all processing so that we get a consistent view
-        mHandlerThread.getLooper().getQueue().postSyncBarrier();
-
         mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
         mImpl.enqueueBroadcastLocked(makeBroadcastRecord(musicVolumeChanged,
                 optionsMusicVolumeChanged));
@@ -1004,9 +1013,6 @@
         final Pair<Intent, BroadcastOptions> dropboxEntryBroadcast3 = createDropboxBroadcast(
                 "TAG_A", now + 2000, 7);
 
-        // Halt all processing so that we get a consistent view
-        mHandlerThread.getLooper().getQueue().postSyncBarrier();
-
         mImpl.enqueueBroadcastLocked(makeBroadcastRecord(dropboxEntryBroadcast1.first,
                 dropboxEntryBroadcast1.second));
         mImpl.enqueueBroadcastLocked(makeBroadcastRecord(dropboxEntryBroadcast2.first,
@@ -1036,9 +1042,6 @@
                 .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
                 .setDeliveryGroupMatchingKey(Intent.ACTION_CLOSE_SYSTEM_DIALOGS, "testing");
 
-        // Halt all processing so that we get a consistent view
-        mHandlerThread.getLooper().getQueue().postSyncBarrier();
-
         mImpl.enqueueBroadcastLocked(makeBroadcastRecord(
                 closeSystemDialogs1, optionsCloseSystemDialog1));
         mImpl.enqueueBroadcastLocked(makeBroadcastRecord(
@@ -1088,9 +1091,6 @@
         final Intent userPresent = new Intent(Intent.ACTION_USER_PRESENT);
         userPresent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
 
-        // Halt all processing so that we get a consistent view
-        mHandlerThread.getLooper().getQueue().postSyncBarrier();
-
         final BroadcastRecord userPresentRecord1 = makeBroadcastRecord(userPresent);
         final BroadcastRecord userPresentRecord2 = makeBroadcastRecord(userPresent);
 
@@ -1129,8 +1129,6 @@
                 makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW)
         ));
 
-        // Halt all processing so that we get a consistent view
-        mHandlerThread.getLooper().getQueue().postSyncBarrier();
         mImpl.enqueueBroadcastLocked(record1);
         mImpl.enqueueBroadcastLocked(record2);
 
@@ -1152,12 +1150,9 @@
         final BroadcastOptions optionsTimeTick = BroadcastOptions.makeBasic();
         optionsTimeTick.setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT);
 
-        // Halt all processing so that we get a consistent view
-        try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
-            mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
-            mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
-        }
-        mImpl.waitForIdle(LOG_WRITER_INFO);
+        mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
+        mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
+        waitForIdle();
 
         // 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 90e6a2d..7be1d7c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -77,6 +77,7 @@
 import android.os.IBinder;
 import android.os.PowerExemptionManager;
 import android.os.SystemClock;
+import android.os.TestLooperManager;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
@@ -146,6 +147,7 @@
 
     private Context mContext;
     private HandlerThread mHandlerThread;
+    private TestLooperManager mLooper;
     private AtomicInteger mNextPid;
 
     @Mock
@@ -206,6 +208,11 @@
 
         mHandlerThread = new HandlerThread(TAG);
         mHandlerThread.start();
+
+        // Pause all event processing until a test chooses to resume
+        mLooper = Objects.requireNonNull(InstrumentationRegistry.getInstrumentation()
+                .acquireLooperManager(mHandlerThread.getLooper()));
+
         mNextPid = new AtomicInteger(100);
 
         LocalServices.removeServiceForTest(DropBoxManagerInternal.class);
@@ -360,25 +367,6 @@
         }
     }
 
-    /**
-     * Helper that leverages try-with-resources to pause dispatch of
-     * {@link #mHandlerThread} until released.
-     */
-    static class SyncBarrier implements AutoCloseable {
-        private final int mToken;
-        private HandlerThread mThread;
-
-        SyncBarrier(HandlerThread thread) {
-            mThread = thread;
-            mToken = mThread.getLooper().getQueue().postSyncBarrier();
-        }
-
-        @Override
-        public void close() throws Exception {
-            mThread.getLooper().getQueue().removeSyncBarrier(mToken);
-        }
-    }
-
     private enum ProcessStartBehavior {
         /** Process starts successfully */
         SUCCESS,
@@ -671,8 +659,15 @@
         }
     }
 
+    /**
+     * Un-pause our handler to process pending events, wait for our queue to go
+     * idle, and then re-pause the handler.
+     */
     private void waitForIdle() throws Exception {
+        mLooper.release();
         mQueue.waitForIdle(LOG_WRITER_INFO);
+        mLooper = Objects.requireNonNull(InstrumentationRegistry.getInstrumentation()
+                .acquireLooperManager(mHandlerThread.getLooper()));
     }
 
     private void verifyScheduleReceiver(ProcessRecord app, Intent intent) throws Exception {
@@ -1156,23 +1151,21 @@
         final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN);
 
         final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
-            enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, new ArrayList<>(
-                    List.of(makeRegisteredReceiver(receiverApp),
-                            makeManifestReceiver(PACKAGE_GREEN, CLASS_RED),
-                            makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
-                            makeManifestReceiver(PACKAGE_GREEN, CLASS_BLUE)))));
+        enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, new ArrayList<>(
+                List.of(makeRegisteredReceiver(receiverApp),
+                        makeManifestReceiver(PACKAGE_GREEN, CLASS_RED),
+                        makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
+                        makeManifestReceiver(PACKAGE_GREEN, CLASS_BLUE)))));
 
-            synchronized (mAms) {
-                mQueue.cleanupDisabledPackageReceiversLocked(PACKAGE_GREEN, Set.of(CLASS_GREEN),
-                        UserHandle.USER_SYSTEM);
+        synchronized (mAms) {
+            mQueue.cleanupDisabledPackageReceiversLocked(PACKAGE_GREEN, Set.of(CLASS_GREEN),
+                    UserHandle.USER_SYSTEM);
 
-                // Also try clearing out other unrelated things that should leave
-                // the final receiver intact
-                mQueue.cleanupDisabledPackageReceiversLocked(PACKAGE_RED, null,
-                        UserHandle.USER_SYSTEM);
-                mQueue.cleanupDisabledPackageReceiversLocked(null, null, USER_GUEST);
-            }
+            // Also try clearing out other unrelated things that should leave
+            // the final receiver intact
+            mQueue.cleanupDisabledPackageReceiversLocked(PACKAGE_RED, null,
+                    UserHandle.USER_SYSTEM);
+            mQueue.cleanupDisabledPackageReceiversLocked(null, null, USER_GUEST);
 
             // To maximize test coverage, dump current state; we're not worried
             // about the actual output, just that we don't crash
@@ -1200,21 +1193,19 @@
 
         final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
         final Intent timeZone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
-        try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
-            enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, USER_GUEST, new ArrayList<>(
-                    List.of(makeRegisteredReceiver(callerApp),
-                            makeManifestReceiver(PACKAGE_GREEN, CLASS_RED, USER_GUEST),
-                            makeManifestReceiver(PACKAGE_BLUE, CLASS_GREEN, USER_GUEST),
-                            makeManifestReceiver(PACKAGE_YELLOW, CLASS_BLUE, USER_GUEST)))));
-            enqueueBroadcast(makeBroadcastRecord(timeZone, callerApp, USER_GUEST, new ArrayList<>(
-                    List.of(makeRegisteredReceiver(callerApp),
-                            makeManifestReceiver(PACKAGE_GREEN, CLASS_RED, USER_GUEST),
-                            makeManifestReceiver(PACKAGE_BLUE, CLASS_GREEN, USER_GUEST),
-                            makeManifestReceiver(PACKAGE_YELLOW, CLASS_BLUE, USER_GUEST)))));
+        enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, USER_GUEST, new ArrayList<>(
+                List.of(makeRegisteredReceiver(callerApp),
+                        makeManifestReceiver(PACKAGE_GREEN, CLASS_RED, USER_GUEST),
+                        makeManifestReceiver(PACKAGE_BLUE, CLASS_GREEN, USER_GUEST),
+                        makeManifestReceiver(PACKAGE_YELLOW, CLASS_BLUE, USER_GUEST)))));
+        enqueueBroadcast(makeBroadcastRecord(timeZone, callerApp, USER_GUEST, new ArrayList<>(
+                List.of(makeRegisteredReceiver(callerApp),
+                        makeManifestReceiver(PACKAGE_GREEN, CLASS_RED, USER_GUEST),
+                        makeManifestReceiver(PACKAGE_BLUE, CLASS_GREEN, USER_GUEST),
+                        makeManifestReceiver(PACKAGE_YELLOW, CLASS_BLUE, USER_GUEST)))));
 
-            synchronized (mAms) {
-                mQueue.cleanupDisabledPackageReceiversLocked(null, null, USER_GUEST);
-            }
+        synchronized (mAms) {
+            mQueue.cleanupDisabledPackageReceiversLocked(null, null, USER_GUEST);
         }
 
         waitForIdle();
@@ -1240,15 +1231,12 @@
         final ProcessRecord oldApp = makeActiveProcessRecord(PACKAGE_GREEN);
 
         final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
-            enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, new ArrayList<>(
-                    List.of(makeRegisteredReceiver(oldApp),
-                            makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)))));
-
-            synchronized (mAms) {
-                oldApp.killLocked(TAG, 42, false);
-                mQueue.onApplicationCleanupLocked(oldApp);
-            }
+        enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, new ArrayList<>(
+                List.of(makeRegisteredReceiver(oldApp),
+                        makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)))));
+        synchronized (mAms) {
+            oldApp.killLocked(TAG, 42, false);
+            mQueue.onApplicationCleanupLocked(oldApp);
         }
         waitForIdle();
 
@@ -1621,17 +1609,14 @@
         final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
         final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
         airplane.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
-        try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
-            enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
-                    List.of(makeRegisteredReceiver(receiverBlueApp, 10),
-                            makeRegisteredReceiver(receiverGreenApp, 10),
-                            makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE),
-                            makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW),
-                            makeRegisteredReceiver(receiverYellowApp, -10))));
-            enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
-                    List.of(makeRegisteredReceiver(receiverBlueApp))));
-        }
-
+        enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
+                List.of(makeRegisteredReceiver(receiverBlueApp, 10),
+                        makeRegisteredReceiver(receiverGreenApp, 10),
+                        makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE),
+                        makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW),
+                        makeRegisteredReceiver(receiverYellowApp, -10))));
+        enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
+                List.of(makeRegisteredReceiver(receiverBlueApp))));
         waitForIdle();
 
         // Ignore the final foreground broadcast
@@ -1745,17 +1730,15 @@
         final IIntentReceiver resultToFirst = mock(IIntentReceiver.class);
         final IIntentReceiver resultToSecond = mock(IIntentReceiver.class);
 
-        try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
-            enqueueBroadcast(makeOrderedBroadcastRecord(timezoneFirst, callerApp,
-                    List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE),
-                            makeManifestReceiver(PACKAGE_BLUE, CLASS_GREEN)),
-                    resultToFirst, null));
-            enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
-                    List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_RED))));
-            enqueueBroadcast(makeOrderedBroadcastRecord(timezoneSecond, callerApp,
-                    List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_GREEN)),
-                    resultToSecond, null));
-        }
+        enqueueBroadcast(makeOrderedBroadcastRecord(timezoneFirst, callerApp,
+                List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE),
+                        makeManifestReceiver(PACKAGE_BLUE, CLASS_GREEN)),
+                resultToFirst, null));
+        enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
+                List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_RED))));
+        enqueueBroadcast(makeOrderedBroadcastRecord(timezoneSecond, callerApp,
+                List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_GREEN)),
+                resultToSecond, null));
 
         waitForIdle();
         final IApplicationThread blueThread = mAms.getProcessRecordLocked(PACKAGE_BLUE,
@@ -1836,14 +1819,12 @@
         timeTickFirst.putExtra(Intent.EXTRA_INDEX, "third");
         timeTickThird.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
 
-        try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
-            enqueueBroadcast(makeBroadcastRecord(timeTickFirst, callerApp,
-                    List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
-            enqueueBroadcast(makeBroadcastRecord(timeTickSecond, callerApp,
-                    List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
-            enqueueBroadcast(makeBroadcastRecord(timeTickThird, callerApp,
-                    List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
-        }
+        enqueueBroadcast(makeBroadcastRecord(timeTickFirst, callerApp,
+                List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
+        enqueueBroadcast(makeBroadcastRecord(timeTickSecond, callerApp,
+                List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
+        enqueueBroadcast(makeBroadcastRecord(timeTickThird, callerApp,
+                List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
 
         waitForIdle();
         final IApplicationThread blueThread = mAms.getProcessRecordLocked(PACKAGE_BLUE,
@@ -1878,26 +1859,26 @@
         assertTrue(mQueue.isIdleLocked());
         assertTrue(mQueue.isBeyondBarrierLocked(beforeFirst));
 
-        try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
-            final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
-            enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
-                    List.of(makeRegisteredReceiver(receiverApp))));
-            afterFirst = SystemClock.uptimeMillis();
+        final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+        enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
+                List.of(makeRegisteredReceiver(receiverApp))));
+        afterFirst = SystemClock.uptimeMillis();
 
-            assertFalse(mQueue.isIdleLocked());
-            assertTrue(mQueue.isBeyondBarrierLocked(beforeFirst));
-            assertFalse(mQueue.isBeyondBarrierLocked(afterFirst));
+        assertFalse(mQueue.isIdleLocked());
+        assertTrue(mQueue.isBeyondBarrierLocked(beforeFirst));
+        assertFalse(mQueue.isBeyondBarrierLocked(afterFirst));
 
-            final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-            enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
-                    List.of(makeRegisteredReceiver(receiverApp))));
-            afterSecond = SystemClock.uptimeMillis() + 10;
+        final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+        enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
+                List.of(makeRegisteredReceiver(receiverApp))));
+        afterSecond = SystemClock.uptimeMillis() + 10;
 
-            assertFalse(mQueue.isIdleLocked());
-            assertTrue(mQueue.isBeyondBarrierLocked(beforeFirst));
-            assertFalse(mQueue.isBeyondBarrierLocked(afterFirst));
-            assertFalse(mQueue.isBeyondBarrierLocked(afterSecond));
-        }
+        assertFalse(mQueue.isIdleLocked());
+        assertTrue(mQueue.isBeyondBarrierLocked(beforeFirst));
+        assertFalse(mQueue.isBeyondBarrierLocked(afterFirst));
+        assertFalse(mQueue.isBeyondBarrierLocked(afterSecond));
+
+        mLooper.release();
 
         mQueue.waitForBarrier(LOG_WRITER_INFO);
         assertTrue(mQueue.isBeyondBarrierLocked(afterFirst));
@@ -2048,25 +2029,23 @@
         final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
 
         final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
-        try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
-            final Object greenReceiver = makeRegisteredReceiver(receiverGreenApp);
-            final Object blueReceiver = makeRegisteredReceiver(receiverBlueApp);
-            final Object yellowReceiver = makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW);
-            final Object orangeReceiver = makeManifestReceiver(PACKAGE_ORANGE, CLASS_ORANGE);
-            enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
-                    List.of(greenReceiver, blueReceiver, yellowReceiver, orangeReceiver)));
+        final Object greenReceiver = makeRegisteredReceiver(receiverGreenApp);
+        final Object blueReceiver = makeRegisteredReceiver(receiverBlueApp);
+        final Object yellowReceiver = makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW);
+        final Object orangeReceiver = makeManifestReceiver(PACKAGE_ORANGE, CLASS_ORANGE);
+        enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
+                List.of(greenReceiver, blueReceiver, yellowReceiver, orangeReceiver)));
 
-            doAnswer(invocation -> {
-                final BroadcastRecord r = invocation.getArgument(0);
-                final Object o = invocation.getArgument(1);
-                if (airplane.getAction().equals(r.intent.getAction())
-                        && (isReceiverEquals(o, greenReceiver)
-                                || isReceiverEquals(o, orangeReceiver))) {
-                    return "test skipped receiver";
-                }
-                return null;
-            }).when(mSkipPolicy).shouldSkipMessage(any(BroadcastRecord.class), any());
-        }
+        doAnswer(invocation -> {
+            final BroadcastRecord r = invocation.getArgument(0);
+            final Object o = invocation.getArgument(1);
+            if (airplane.getAction().equals(r.intent.getAction())
+                    && (isReceiverEquals(o, greenReceiver)
+                            || isReceiverEquals(o, orangeReceiver))) {
+                return "test skipped receiver";
+            }
+            return null;
+        }).when(mSkipPolicy).shouldSkipMessage(any(BroadcastRecord.class), any());
 
         waitForIdle();
         // Verify that only blue and yellow receiver apps received the broadcast.
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/BackupAndRestoreFeatureFlagsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/BackupAndRestoreFeatureFlagsTest.java
index 201da35..9f685b4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/BackupAndRestoreFeatureFlagsTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/BackupAndRestoreFeatureFlagsTest.java
@@ -104,4 +104,24 @@
         assertThat(BackupAndRestoreFeatureFlags.getFullBackupUtilsRouteBufferSizeBytes())
                 .isEqualTo(5678);
     }
+
+    @Test
+    public void getUnifiedRestoreContinueAfterTransportFailureInKvRestore_notSet_returnsDefault() {
+        assertThat(
+                BackupAndRestoreFeatureFlags
+                        .getUnifiedRestoreContinueAfterTransportFailureInKvRestore())
+                .isEqualTo(true);
+    }
+
+    @Test
+    public void getUnifiedRestoreContinueAfterTransportFailureInKvRestore_set_returnsSetValue() {
+        DeviceConfig.setProperty(/*namespace=*/ "backup_and_restore",
+                /*name=*/ "unified_restore_continue_after_transport_failure_in_kv_restore",
+                /*value=*/ "false", /*makeDefault=*/ false);
+
+        assertThat(
+                BackupAndRestoreFeatureFlags
+                        .getUnifiedRestoreContinueAfterTransportFailureInKvRestore())
+                .isEqualTo(false);
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
index 017c939..c84797f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
@@ -25,20 +25,33 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.when;
 
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupTransport;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.os.Message;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
+
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
 
-import android.app.backup.BackupDataInput;
-import android.app.backup.BackupDataOutput;
-import android.platform.test.annotations.Presubmit;
-
+import com.android.modules.utils.testing.TestableDeviceConfig;
 import com.android.server.backup.UserBackupManagerService;
+import com.android.server.backup.internal.BackupHandler;
+import com.android.server.backup.transport.BackupTransportClient;
+import com.android.server.backup.transport.TransportConnection;
+import com.android.server.backup.transport.TransportNotAvailableException;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
@@ -62,9 +75,14 @@
     private static final String SYSTEM_PACKAGE_NAME = "android";
     private static final String NON_SYSTEM_PACKAGE_NAME = "package";
 
-    @Mock private BackupDataInput mBackupDataInput;
-    @Mock private BackupDataOutput mBackupDataOutput;
-    @Mock private UserBackupManagerService mBackupManagerService;
+    @Mock
+    private BackupDataInput mBackupDataInput;
+    @Mock
+    private BackupDataOutput mBackupDataOutput;
+    @Mock
+    private UserBackupManagerService mBackupManagerService;
+    @Mock
+    private TransportConnection mTransportConnection;
 
     private Set<String> mExcludedkeys = new HashSet<>();
     private Map<String, String> mBackupData = new HashMap<>();
@@ -74,12 +92,20 @@
     private Set<String> mBackupDataDump;
     private PerformUnifiedRestoreTask mRestoreTask;
 
+    @Rule
+    public TestableDeviceConfig.TestableDeviceConfigRule
+            mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule();
+
+    private Context mContext;
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
         populateTestData();
 
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
         mBackupDataSource = new ArrayDeque<>(mBackupData.keySet());
         when(mBackupDataInput.readNextHeader()).then(new Answer<Boolean>() {
             @Override
@@ -106,7 +132,7 @@
                     }
                 });
 
-        mRestoreTask = new PerformUnifiedRestoreTask(mBackupManagerService);
+        mRestoreTask = new PerformUnifiedRestoreTask(mBackupManagerService, mTransportConnection);
     }
 
     private void populateTestData() {
@@ -179,4 +205,64 @@
 
         assertTrue(mRestoreTask.shouldStageBackupData(SYSTEM_PACKAGE_NAME));
     }
+
+    @Test
+    public void testFailedKeyValueRestore_continueAfterFeatureEnabled_nextStateIsRunningQueue()
+            throws TransportNotAvailableException, RemoteException {
+        DeviceConfig.setProperty(
+                "backup_and_restore",
+                "unified_restore_continue_after_transport_failure_in_kv_restore",
+                "true",
+                false);
+
+        setupForRestoreKeyValueState(BackupTransport.TRANSPORT_ERROR);
+
+        mRestoreTask.setCurrentUnifiedRestoreStateForTesting(UnifiedRestoreState.RESTORE_KEYVALUE);
+        mRestoreTask.setStateDirForTesting(mContext.getCacheDir());
+
+        PackageInfo testPackageInfo = new PackageInfo();
+        testPackageInfo.packageName = "test.package.name";
+        mRestoreTask.initiateOneRestoreForTesting(testPackageInfo, 0L);
+        assertTrue(
+                mRestoreTask.getCurrentUnifiedRestoreStateForTesting()
+                        == UnifiedRestoreState.RUNNING_QUEUE);
+    }
+
+    @Test
+    public void testFailedKeyValueRestore_continueAfterFeatureDisabled_nextStateIsFinal()
+            throws RemoteException, TransportNotAvailableException {
+        DeviceConfig.setProperty(
+                "backup_and_restore",
+                "unified_restore_continue_after_transport_failure_in_kv_restore",
+                "false",
+                false);
+
+        setupForRestoreKeyValueState(BackupTransport.TRANSPORT_ERROR);
+
+        mRestoreTask.setCurrentUnifiedRestoreStateForTesting(UnifiedRestoreState.RESTORE_KEYVALUE);
+        mRestoreTask.setStateDirForTesting(mContext.getCacheDir());
+
+        PackageInfo testPackageInfo = new PackageInfo();
+        testPackageInfo.packageName = "test.package.name";
+        mRestoreTask.initiateOneRestoreForTesting(testPackageInfo, 0L);
+        assertTrue(
+                mRestoreTask.getCurrentUnifiedRestoreStateForTesting()
+                        == UnifiedRestoreState.FINAL);
+    }
+
+    private void setupForRestoreKeyValueState(int transportStatus)
+            throws RemoteException, TransportNotAvailableException {
+        // Mock BackupHandler to do nothing when executeNextState() is called
+        BackupHandler backupHandler = Mockito.mock(BackupHandler.class);
+        when(backupHandler.obtainMessage(anyInt(), any())).thenReturn(new Message());
+        when(backupHandler.sendMessage(any())).thenReturn(true);
+
+        // Return cache directory for any bookkeeping or maintaining persistent state.
+        when(mBackupManagerService.getDataDir()).thenReturn(mContext.getCacheDir());
+        when(mBackupManagerService.getBackupHandler()).thenReturn(backupHandler);
+
+        BackupTransportClient transport = Mockito.mock(BackupTransportClient.class);
+        when(transport.getRestoreData(any())).thenReturn(transportStatus);
+        when(mTransportConnection.connectOrThrow(any())).thenReturn(transport);
+    }
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index 45fefe4..ca857f1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -38,9 +38,7 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.when;
 
-import android.content.ContentResolver;
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.res.Resources;
 import android.hardware.Sensor;
 import android.hardware.SensorEventListener;
@@ -51,18 +49,18 @@
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.SystemProperties;
-import android.os.UserHandle;
 import android.os.test.TestLooper;
 import android.provider.Settings;
+import android.testing.TestableContext;
 import android.util.FloatProperty;
 import android.view.Display;
 import android.view.DisplayInfo;
 
-import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
 
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.server.ExtendedMockitoRule;
 import com.android.server.LocalServices;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.display.RampAnimator.DualRampAnimator;
@@ -74,12 +72,12 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.Mock;
-import org.mockito.MockitoSession;
 import org.mockito.quality.Strictness;
 import org.mockito.stubbing.Answer;
 
@@ -97,11 +95,9 @@
     private static final String SECOND_FOLLOWER_UNIQUE_DISPLAY_ID = "unique_id_789";
     private static final float PROX_SENSOR_MAX_RANGE = 5;
 
-    private MockitoSession mSession;
     private OffsettableClock mClock;
     private TestLooper mTestLooper;
     private Handler mHandler;
-    private Context mContextSpy;
     private DisplayPowerControllerHolder mHolder;
     private Sensor mProxSensor;
 
@@ -118,40 +114,44 @@
     @Mock
     private PowerManager mPowerManagerMock;
     @Mock
-    private Resources mResourcesMock;
-    @Mock
     private ColorDisplayService.ColorDisplayServiceInternal mCdsiMock;
 
     @Captor
     private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
 
+    @Rule
+    public final TestableContext mContext = new TestableContext(
+            InstrumentationRegistry.getInstrumentation().getContext());
+
+    @Rule
+    public final ExtendedMockitoRule mExtendedMockitoRule =
+            new ExtendedMockitoRule.Builder(this)
+                    .setStrictness(Strictness.LENIENT)
+                    .spyStatic(SystemProperties.class)
+                    .spyStatic(BatteryStatsService.class)
+                    .build();
+
     @Before
     public void setUp() throws Exception {
-        mSession = ExtendedMockito.mockitoSession()
-                .initMocks(this)
-                .strictness(Strictness.LENIENT)
-                .spyStatic(SystemProperties.class)
-                .spyStatic(LocalServices.class)
-                .spyStatic(BatteryStatsService.class)
-                .spyStatic(Settings.System.class)
-                .startMocking();
-        mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
         mClock = new OffsettableClock.Stopped();
         mTestLooper = new TestLooper(mClock::now);
         mHandler = new Handler(mTestLooper.getLooper());
-        addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
 
-        when(mContextSpy.getSystemService(eq(PowerManager.class))).thenReturn(mPowerManagerMock);
-        when(mContextSpy.getResources()).thenReturn(mResourcesMock);
+        // Put the system into manual brightness by default, just to minimize unexpected events and
+        // have a consistent starting state
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+
+        addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
+        addLocalServiceMock(ColorDisplayService.ColorDisplayServiceInternal.class,
+                mCdsiMock);
+
+        mContext.addMockSystemService(PowerManager.class, mPowerManagerMock);
 
         doAnswer((Answer<Void>) invocationOnMock -> null).when(() ->
                 SystemProperties.set(anyString(), any()));
-        doAnswer((Answer<ColorDisplayService.ColorDisplayServiceInternal>) invocationOnMock ->
-                mCdsiMock).when(() -> LocalServices.getService(
-                ColorDisplayService.ColorDisplayServiceInternal.class));
         doAnswer((Answer<Void>) invocationOnMock -> null).when(BatteryStatsService::getService);
-        doAnswer((Answer<Boolean>) invocationOnMock -> true).when(() ->
-                Settings.System.putFloatForUser(any(), any(), anyFloat(), anyInt()));
 
         setUpSensors();
         mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
@@ -159,8 +159,8 @@
 
     @After
     public void tearDown() {
-        mSession.finishMocking();
         LocalServices.removeServiceForTest(WindowManagerPolicy.class);
+        LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
     }
 
     @Test
@@ -421,11 +421,9 @@
 
     @Test
     public void testDisplayBrightnessFollowers_AutomaticBrightness() {
-        doAnswer((Answer<Integer>) invocationOnMock ->
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
-                .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
-                        eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
-                        eq(UserHandle.USER_CURRENT)));
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
         final float brightness = 0.4f;
         final float nits = 300;
         final float ambientLux = 3000;
@@ -436,7 +434,7 @@
         when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits);
         when(mHolder.automaticBrightnessController.getAmbientLux()).thenReturn(ambientLux);
         when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
-        DisplayPowerController followerDpc = mock(DisplayPowerController.class);
+        DisplayPowerController2 followerDpc = mock(DisplayPowerController2.class);
 
         mHolder.dpc.addDisplayBrightnessFollower(followerDpc);
         DisplayPowerRequest dpr = new DisplayPowerRequest();
@@ -542,11 +540,9 @@
 
     @Test
     public void testSetScreenOffBrightnessSensorEnabled_DisplayIsOff() {
-        doAnswer((Answer<Integer>) invocationOnMock ->
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
-                .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
-                        eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
-                        eq(UserHandle.USER_CURRENT)));
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
 
         DisplayPowerRequest dpr = new DisplayPowerRequest();
         dpr.policy = DisplayPowerRequest.POLICY_OFF;
@@ -577,17 +573,14 @@
 
     @Test
     public void testSetScreenOffBrightnessSensorEnabled_DisplayIsInDoze() {
-        doAnswer((Answer<Integer>) invocationOnMock ->
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
-                .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
-                        eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
-                        eq(UserHandle.USER_CURRENT)));
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
 
         DisplayPowerRequest dpr = new DisplayPowerRequest();
         dpr.policy = DisplayPowerRequest.POLICY_DOZE;
-        when(mResourcesMock.getBoolean(
-                com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing))
-                .thenReturn(true);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true);
         mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
         advanceTime(1); // Run updatePowerState
 
@@ -615,12 +608,7 @@
 
     @Test
     public void testSetScreenOffBrightnessSensorDisabled_AutoBrightnessIsDisabled() {
-        doAnswer((Answer<Integer>) invocationOnMock ->
-                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL)
-                .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
-                        eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
-                        eq(UserHandle.USER_CURRENT)));
-
+        // Tests are set up with manual brightness by default, so no need to set it here.
         DisplayPowerRequest dpr = new DisplayPowerRequest();
         dpr.policy = DisplayPowerRequest.POLICY_OFF;
         mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
@@ -632,11 +620,9 @@
 
     @Test
     public void testSetScreenOffBrightnessSensorDisabled_DisplayIsDisabled() {
-        doAnswer((Answer<Integer>) invocationOnMock ->
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
-                .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
-                        eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
-                        eq(UserHandle.USER_CURRENT)));
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
         mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ false);
 
         DisplayPowerRequest dpr = new DisplayPowerRequest();
@@ -690,9 +676,9 @@
     public void testBrightnessNitsPersistWhenDisplayDeviceChanges() {
         float brightness = 0.3f;
         float nits = 500;
-        when(mResourcesMock.getBoolean(
-                com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay))
-                .thenReturn(true);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay,
+                true);
         mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
         when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits);
 
@@ -753,7 +739,7 @@
                 any(HysteresisLevels.class),
                 any(HysteresisLevels.class),
                 any(HysteresisLevels.class),
-                eq(mContextSpy),
+                eq(mContext),
                 any(HighBrightnessModeController.class),
                 any(BrightnessThrottler.class),
                 isNull(),
@@ -862,7 +848,7 @@
         setUpDisplay(displayId, uniqueId, display, device, config, isEnabled);
 
         final DisplayPowerController2 dpc = new DisplayPowerController2(
-                mContextSpy, injector, mDisplayPowerCallbacksMock, mHandler,
+                mContext, injector, mDisplayPowerCallbacksMock, mHandler,
                 mSensorManagerMock, mDisplayBlankerMock, display,
                 mBrightnessTrackerMock, brightnessSetting, () -> {},
                 hbmMetadata, /* bootCompleted= */ false);
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
index d9133a4..0b97c5c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -38,9 +38,7 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.when;
 
-import android.content.ContentResolver;
 import android.content.Context;
-import android.content.ContextWrapper;
 import android.content.res.Resources;
 import android.hardware.Sensor;
 import android.hardware.SensorEventListener;
@@ -51,18 +49,18 @@
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.SystemProperties;
-import android.os.UserHandle;
 import android.os.test.TestLooper;
 import android.provider.Settings;
+import android.testing.TestableContext;
 import android.util.FloatProperty;
 import android.view.Display;
 import android.view.DisplayInfo;
 
-import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
 
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.server.ExtendedMockitoRule;
 import com.android.server.LocalServices;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.display.RampAnimator.DualRampAnimator;
@@ -75,12 +73,12 @@
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Captor;
 import org.mockito.Mock;
-import org.mockito.MockitoSession;
 import org.mockito.quality.Strictness;
 import org.mockito.stubbing.Answer;
 
@@ -98,11 +96,9 @@
     private static final String SECOND_FOLLOWER_UNIQUE_DISPLAY_ID = "unique_id_789";
     private static final float PROX_SENSOR_MAX_RANGE = 5;
 
-    private MockitoSession mSession;
     private OffsettableClock mClock;
     private TestLooper mTestLooper;
     private Handler mHandler;
-    private Context mContextSpy;
     private DisplayPowerControllerHolder mHolder;
     private Sensor mProxSensor;
 
@@ -119,41 +115,45 @@
     @Mock
     private PowerManager mPowerManagerMock;
     @Mock
-    private Resources mResourcesMock;
-    @Mock
     private ColorDisplayService.ColorDisplayServiceInternal mCdsiMock;
 
     @Captor
     private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
 
+    @Rule
+    public final TestableContext mContext = new TestableContext(
+            InstrumentationRegistry.getInstrumentation().getContext());
+
+    @Rule
+    public final ExtendedMockitoRule mExtendedMockitoRule =
+            new ExtendedMockitoRule.Builder(this)
+                    .setStrictness(Strictness.LENIENT)
+                    .spyStatic(SystemProperties.class)
+                    .spyStatic(BatteryStatsService.class)
+                    .build();
+
     @Before
     public void setUp() throws Exception {
-        mSession = ExtendedMockito.mockitoSession()
-                .initMocks(this)
-                .strictness(Strictness.LENIENT)
-                .spyStatic(SystemProperties.class)
-                .spyStatic(LocalServices.class)
-                .spyStatic(BatteryStatsService.class)
-                .spyStatic(Settings.System.class)
-                .startMocking();
-        mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
         mClock = new OffsettableClock.Stopped();
         mTestLooper = new TestLooper(mClock::now);
         mHandler = new Handler(mTestLooper.getLooper());
 
-        addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
+        // Put the system into manual brightness by default, just to minimize unexpected events and
+        // have a consistent starting state
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
 
-        when(mContextSpy.getSystemService(eq(PowerManager.class))).thenReturn(mPowerManagerMock);
-        when(mContextSpy.getResources()).thenReturn(mResourcesMock);
+
+        addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
+        addLocalServiceMock(ColorDisplayService.ColorDisplayServiceInternal.class,
+                mCdsiMock);
+
+        mContext.addMockSystemService(PowerManager.class, mPowerManagerMock);
 
         doAnswer((Answer<Void>) invocationOnMock -> null).when(() ->
                 SystemProperties.set(anyString(), any()));
-        doAnswer((Answer<ColorDisplayService.ColorDisplayServiceInternal>) invocationOnMock ->
-                mCdsiMock).when(() -> LocalServices.getService(
-                ColorDisplayService.ColorDisplayServiceInternal.class));
         doAnswer((Answer<Void>) invocationOnMock -> null).when(BatteryStatsService::getService);
-        doAnswer((Answer<Boolean>) invocationOnMock -> true).when(() ->
-                Settings.System.putFloatForUser(any(), any(), anyFloat(), anyInt()));
 
         setUpSensors();
         mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
@@ -161,8 +161,8 @@
 
     @After
     public void tearDown() {
-        mSession.finishMocking();
         LocalServices.removeServiceForTest(WindowManagerPolicy.class);
+        LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
     }
 
     @Test
@@ -425,11 +425,9 @@
 
     @Test
     public void testDisplayBrightnessFollowers_AutomaticBrightness() {
-        doAnswer((Answer<Integer>) invocationOnMock ->
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
-                .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
-                        eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
-                        eq(UserHandle.USER_CURRENT)));
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
         final float brightness = 0.4f;
         final float nits = 300;
         final float ambientLux = 3000;
@@ -547,11 +545,9 @@
 
     @Test
     public void testSetScreenOffBrightnessSensorEnabled_DisplayIsOff() {
-        doAnswer((Answer<Integer>) invocationOnMock ->
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
-                .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
-                        eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
-                        eq(UserHandle.USER_CURRENT)));
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
 
         DisplayPowerRequest dpr = new DisplayPowerRequest();
         dpr.policy = DisplayPowerRequest.POLICY_OFF;
@@ -582,17 +578,14 @@
 
     @Test
     public void testSetScreenOffBrightnessSensorEnabled_DisplayIsInDoze() {
-        doAnswer((Answer<Integer>) invocationOnMock ->
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
-                .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
-                        eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
-                        eq(UserHandle.USER_CURRENT)));
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
 
         DisplayPowerRequest dpr = new DisplayPowerRequest();
         dpr.policy = DisplayPowerRequest.POLICY_DOZE;
-        when(mResourcesMock.getBoolean(
-                com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing))
-                .thenReturn(true);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true);
         mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
         advanceTime(1); // Run updatePowerState
 
@@ -620,12 +613,7 @@
 
     @Test
     public void testSetScreenOffBrightnessSensorDisabled_AutoBrightnessIsDisabled() {
-        doAnswer((Answer<Integer>) invocationOnMock ->
-                Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL)
-                .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
-                        eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
-                        eq(UserHandle.USER_CURRENT)));
-
+        // Tests are set up with manual brightness by default, so no need to set it here.
         DisplayPowerRequest dpr = new DisplayPowerRequest();
         dpr.policy = DisplayPowerRequest.POLICY_OFF;
         mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
@@ -637,11 +625,10 @@
 
     @Test
     public void testSetScreenOffBrightnessSensorDisabled_DisplayIsDisabled() {
-        doAnswer((Answer<Integer>) invocationOnMock ->
-                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
-                .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
-                        eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
-                        eq(UserHandle.USER_CURRENT)));
+        Settings.System.putInt(mContext.getContentResolver(),
+                Settings.System.SCREEN_BRIGHTNESS_MODE,
+                Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+
         mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ false);
 
         DisplayPowerRequest dpr = new DisplayPowerRequest();
@@ -695,9 +682,10 @@
     public void testBrightnessNitsPersistWhenDisplayDeviceChanges() {
         float brightness = 0.3f;
         float nits = 500;
-        when(mResourcesMock.getBoolean(
-                com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay))
-                .thenReturn(true);
+        mContext.getOrCreateTestableResources().addOverride(
+                com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay,
+                true);
+
         mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
         when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits);
 
@@ -758,7 +746,7 @@
                 any(HysteresisLevels.class),
                 any(HysteresisLevels.class),
                 any(HysteresisLevels.class),
-                eq(mContextSpy),
+                eq(mContext),
                 any(HighBrightnessModeController.class),
                 any(BrightnessThrottler.class),
                 isNull(),
@@ -866,7 +854,7 @@
         setUpDisplay(displayId, uniqueId, display, device, config, isEnabled);
 
         final DisplayPowerController dpc = new DisplayPowerController(
-                mContextSpy, injector, mDisplayPowerCallbacksMock, mHandler,
+                mContext, injector, mDisplayPowerCallbacksMock, mHandler,
                 mSensorManagerMock, mDisplayBlankerMock, display,
                 mBrightnessTrackerMock, brightnessSetting, () -> {},
                 hbmMetadata, /* bootCompleted= */ false);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobNotificationCoordinatorTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobNotificationCoordinatorTest.java
index df2f59a..e24354f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobNotificationCoordinatorTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobNotificationCoordinatorTest.java
@@ -149,7 +149,8 @@
                 .enqueueNotification(eq(TEST_PACKAGE), eq(TEST_PACKAGE), eq(uid), eq(pid), any(),
                         eq(notificationId), eq(notification), eq(UserHandle.getUserId(uid)));
 
-        coordinator.removeNotificationAssociation(jsc, JobParameters.STOP_REASON_UNDEFINED);
+        coordinator.removeNotificationAssociation(jsc, JobParameters.STOP_REASON_UNDEFINED,
+                jsc.getRunningJobLocked());
         verify(mNotificationManagerInternal, never())
                 .cancelNotification(anyString(), anyString(), anyInt(), anyInt(), any(),
                         anyInt(), anyInt());
@@ -170,7 +171,8 @@
                 .enqueueNotification(eq(TEST_PACKAGE), eq(TEST_PACKAGE), eq(uid), eq(pid), any(),
                         eq(notificationId), eq(notification), eq(UserHandle.getUserId(uid)));
 
-        coordinator.removeNotificationAssociation(jsc, JobParameters.STOP_REASON_UNDEFINED);
+        coordinator.removeNotificationAssociation(jsc, JobParameters.STOP_REASON_UNDEFINED,
+                jsc.getRunningJobLocked());
         verify(mNotificationManagerInternal)
                 .cancelNotification(eq(TEST_PACKAGE), eq(TEST_PACKAGE), eq(uid), eq(pid), any(),
                         eq(notificationId), eq(UserHandle.getUserId(uid)));
@@ -292,7 +294,8 @@
                         eq(notificationId2), eq(notification2), eq(UserHandle.getUserId(uid)));
 
         // Remove the first job. Only the first notification should be removed.
-        coordinator.removeNotificationAssociation(jsc1, JobParameters.STOP_REASON_UNDEFINED);
+        coordinator.removeNotificationAssociation(jsc1, JobParameters.STOP_REASON_UNDEFINED,
+                jsc1.getRunningJobLocked());
         inOrder.verify(mNotificationManagerInternal)
                 .cancelNotification(eq(TEST_PACKAGE), eq(TEST_PACKAGE), eq(uid), eq(pid), any(),
                         eq(notificationId1), eq(UserHandle.getUserId(uid)));
@@ -300,7 +303,8 @@
                 .cancelNotification(anyString(), anyString(), anyInt(), anyInt(), any(),
                         eq(notificationId2), anyInt());
 
-        coordinator.removeNotificationAssociation(jsc2, JobParameters.STOP_REASON_UNDEFINED);
+        coordinator.removeNotificationAssociation(jsc2, JobParameters.STOP_REASON_UNDEFINED,
+                jsc2.getRunningJobLocked());
         inOrder.verify(mNotificationManagerInternal)
                 .cancelNotification(eq(TEST_PACKAGE), eq(TEST_PACKAGE), eq(uid), eq(pid), any(),
                         eq(notificationId2), eq(UserHandle.getUserId(uid)));
@@ -335,12 +339,14 @@
                         eq(notificationId), eq(notification2), eq(UserHandle.getUserId(uid)));
 
         // Remove the first job. The notification shouldn't be touched because of the 2nd job.
-        coordinator.removeNotificationAssociation(jsc1, JobParameters.STOP_REASON_UNDEFINED);
+        coordinator.removeNotificationAssociation(jsc1, JobParameters.STOP_REASON_UNDEFINED,
+                jsc1.getRunningJobLocked());
         inOrder.verify(mNotificationManagerInternal, never())
                 .cancelNotification(anyString(), anyString(), anyInt(), anyInt(), any(),
                         anyInt(), anyInt());
 
-        coordinator.removeNotificationAssociation(jsc2, JobParameters.STOP_REASON_UNDEFINED);
+        coordinator.removeNotificationAssociation(jsc2, JobParameters.STOP_REASON_UNDEFINED,
+                jsc2.getRunningJobLocked());
         inOrder.verify(mNotificationManagerInternal)
                 .cancelNotification(eq(TEST_PACKAGE), eq(TEST_PACKAGE), eq(uid), eq(pid), any(),
                         eq(notificationId), eq(UserHandle.getUserId(uid)));
@@ -376,7 +382,8 @@
                         eq(notificationId), eq(notification2), eq(UserHandle.getUserId(uid2)));
 
         // Remove the first job. Only the first notification should be removed.
-        coordinator.removeNotificationAssociation(jsc1, JobParameters.STOP_REASON_UNDEFINED);
+        coordinator.removeNotificationAssociation(jsc1, JobParameters.STOP_REASON_UNDEFINED,
+                jsc1.getRunningJobLocked());
         inOrder.verify(mNotificationManagerInternal)
                 .cancelNotification(eq(TEST_PACKAGE), eq(TEST_PACKAGE), eq(uid1), eq(pid), any(),
                         eq(notificationId), eq(UserHandle.getUserId(uid1)));
@@ -384,7 +391,8 @@
                 .cancelNotification(anyString(), anyString(), eq(uid2), anyInt(), any(),
                         anyInt(), anyInt());
 
-        coordinator.removeNotificationAssociation(jsc2, JobParameters.STOP_REASON_UNDEFINED);
+        coordinator.removeNotificationAssociation(jsc2, JobParameters.STOP_REASON_UNDEFINED,
+                jsc2.getRunningJobLocked());
         inOrder.verify(mNotificationManagerInternal)
                 .cancelNotification(eq(TEST_PACKAGE), eq(TEST_PACKAGE), eq(uid2), eq(pid), any(),
                         eq(notificationId), eq(UserHandle.getUserId(uid2)));
@@ -421,7 +429,8 @@
                         eq(notificationId), eq(notification2), eq(UserHandle.getUserId(uid)));
 
         // Remove the first job. Only the first notification should be removed.
-        coordinator.removeNotificationAssociation(jsc1, JobParameters.STOP_REASON_UNDEFINED);
+        coordinator.removeNotificationAssociation(jsc1, JobParameters.STOP_REASON_UNDEFINED,
+                jsc1.getRunningJobLocked());
         inOrder.verify(mNotificationManagerInternal)
                 .cancelNotification(eq(pkg1), eq(pkg1), eq(uid), eq(pid), any(),
                         eq(notificationId), eq(UserHandle.getUserId(uid)));
@@ -429,7 +438,8 @@
                 .cancelNotification(anyString(), anyString(), eq(uid), anyInt(), any(),
                         anyInt(), anyInt());
 
-        coordinator.removeNotificationAssociation(jsc2, JobParameters.STOP_REASON_UNDEFINED);
+        coordinator.removeNotificationAssociation(jsc2, JobParameters.STOP_REASON_UNDEFINED,
+                jsc2.getRunningJobLocked());
         inOrder.verify(mNotificationManagerInternal)
                 .cancelNotification(eq(pkg2), eq(pkg2), eq(uid), eq(pid), any(),
                         eq(notificationId), eq(UserHandle.getUserId(uid)));
@@ -450,7 +460,8 @@
                 .enqueueNotification(eq(TEST_PACKAGE), eq(TEST_PACKAGE), eq(uid), eq(pid), any(),
                         eq(notificationId), eq(notification), eq(UserHandle.getUserId(uid)));
 
-        coordinator.removeNotificationAssociation(jsc, JobParameters.STOP_REASON_USER);
+        coordinator.removeNotificationAssociation(jsc, JobParameters.STOP_REASON_USER,
+                jsc.getRunningJobLocked());
         verify(mNotificationManagerInternal)
                 .cancelNotification(eq(TEST_PACKAGE), eq(TEST_PACKAGE), eq(uid), eq(pid), any(),
                         eq(notificationId), eq(UserHandle.getUserId(uid)));
@@ -485,12 +496,14 @@
                         eq(notificationId), eq(notification2), eq(UserHandle.getUserId(uid)));
 
         // Remove the first job. The notification shouldn't be touched because of the 2nd job.
-        coordinator.removeNotificationAssociation(jsc1, JobParameters.STOP_REASON_USER);
+        coordinator.removeNotificationAssociation(jsc1, JobParameters.STOP_REASON_USER,
+                jsc1.getRunningJobLocked());
         inOrder.verify(mNotificationManagerInternal, never())
                 .cancelNotification(anyString(), anyString(), anyInt(), anyInt(), any(),
                         anyInt(), anyInt());
 
-        coordinator.removeNotificationAssociation(jsc2, JobParameters.STOP_REASON_USER);
+        coordinator.removeNotificationAssociation(jsc2, JobParameters.STOP_REASON_USER,
+                jsc2.getRunningJobLocked());
         inOrder.verify(mNotificationManagerInternal)
                 .cancelNotification(eq(TEST_PACKAGE), eq(TEST_PACKAGE), eq(uid), eq(pid), any(),
                         eq(notificationId), eq(UserHandle.getUserId(uid)));
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java
index 02fdfad..754f409 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java
@@ -276,9 +276,9 @@
         assertFalse(mThermalStatusRestriction.isJobRestricted(ejRunning));
         assertTrue(mThermalStatusRestriction.isJobRestricted(ejRunningLong));
         assertFalse(mThermalStatusRestriction.isJobRestricted(ui));
-        assertTrue(mThermalStatusRestriction.isJobRestricted(uiRetried));
+        assertFalse(mThermalStatusRestriction.isJobRestricted(uiRetried));
         assertFalse(mThermalStatusRestriction.isJobRestricted(uiRunning));
-        assertTrue(mThermalStatusRestriction.isJobRestricted(uiRunningLong));
+        assertFalse(mThermalStatusRestriction.isJobRestricted(uiRunningLong));
 
         mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_SEVERE);
 
diff --git a/services/tests/servicestests/res/xml/irq_device_map_3.xml b/services/tests/servicestests/res/xml/irq_device_map_3.xml
index 1d2a7d3..7e2529a 100644
--- a/services/tests/servicestests/res/xml/irq_device_map_3.xml
+++ b/services/tests/servicestests/res/xml/irq_device_map_3.xml
@@ -23,4 +23,7 @@
     <device name="test.wifi.device">
         <subsystem>Wifi</subsystem>
     </device>
+    <device name="test.sound_trigger.device">
+        <subsystem>Sound_trigger</subsystem>
+    </device>
 </irq-device-map>
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index 120ddf6..b539a76 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -86,6 +86,7 @@
 
     // Test setting default restrictions for managed profile.
     @Test
+    @Ignore("b/277916462")
     public void testMigration_managedProfileOwner() throws Exception {
         // Create a managed profile user.
         final File user10dir = getServices().addUser(10, 0,
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index dd81abe..16aadac 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -1368,6 +1368,7 @@
     }
 
     @Test
+    @Ignore("b/277916462")
     public void testClearDeviceOwner() throws Exception {
         mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
         mContext.callerPermissions.add(permission.MANAGE_USERS);
@@ -1955,6 +1956,7 @@
     }
 
     @Test
+    @Ignore("b/277916462")
     public void testSetUserRestriction_asDo() throws Exception {
         mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
         mContext.callerPermissions.add(permission.MANAGE_USERS);
@@ -2123,6 +2125,7 @@
     }
 
     @Test
+    @Ignore("b/277916462")
     public void testSetUserRestriction_asPo() {
         setAsProfileOwner(admin1);
 
@@ -2259,6 +2262,7 @@
             );
 
     @Test
+    @Ignore("b/277916462")
     public void testSetUserRestriction_asPoOfOrgOwnedDevice() throws Exception {
         final int MANAGED_PROFILE_ADMIN_UID =
                 UserHandle.getUid(CALLER_USER_HANDLE, DpmMockContext.SYSTEM_UID);
@@ -2334,6 +2338,7 @@
     }
 
     @Test
+    @Ignore("b/277916462")
     public void testNoDefaultEnabledUserRestrictions() throws Exception {
         mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
         mContext.callerPermissions.add(permission.MANAGE_USERS);
@@ -2925,6 +2930,7 @@
     }
 
     @Test
+    @Ignore("b/277916462")
     public void testCreateAdminSupportIntent() throws Exception {
         // Setup device owner.
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
@@ -4993,6 +4999,7 @@
     }
 
     @Test
+    @Ignore("b/277916462")
     public void testWipeDataManagedProfileOnOrganizationOwnedDevice() throws Exception {
         setupProfileOwner();
         configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);
@@ -7023,6 +7030,7 @@
     }
 
     @Test
+    @Ignore("b/277916462")
     public void testSetUserControlDisabledPackages_asDO() throws Exception {
         final List<String> testPackages = new ArrayList<>();
         testPackages.add("package_1");
@@ -7038,6 +7046,7 @@
     }
 
     @Test
+    @Ignore("b/277916462")
     public void testSetUserControlDisabledPackages_asPO() {
         final List<String> testPackages = new ArrayList<>();
         testPackages.add("package_1");
@@ -7776,6 +7785,7 @@
     }
 
     @Test
+    @Ignore("b/277916462")
     public void testSetUserRestriction_financeDo_validRestrictions_setsRestriction()
             throws Exception {
         setDeviceOwner();
@@ -7858,6 +7868,7 @@
     }
 
     @Test
+    @Ignore("b/277916462")
     public void testSetUninstallBlocked_financeDo_success() throws Exception {
         String packageName = "com.android.foo.package";
         setDeviceOwner();
@@ -7871,6 +7882,7 @@
     }
 
     @Test
+    @Ignore("b/277916462")
     public void testSetUserControlDisabledPackages_financeDo_success() throws Exception {
         List<String> packages = new ArrayList<>();
         packages.add("com.android.foo.package");
@@ -7960,6 +7972,7 @@
     }
 
     @Test
+    @Ignore("b/277916462")
     public void testAddPersistentPreferredActivity_financeDo_success() throws Exception {
         IntentFilter filter = new IntentFilter();
         ComponentName target = new ComponentName(admin2.getPackageName(), "test.class");
@@ -7975,6 +7988,7 @@
     }
 
     @Test
+    @Ignore("b/277916462")
     public void testClearPackagePersistentPreferredActvities_financeDo_success() throws Exception {
         String packageName = admin2.getPackageName();
         setDeviceOwner();
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index a0fb3de..21a11bc 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -763,7 +763,7 @@
                 ArgumentCaptor.forClass(DreamManagerInternal.DreamManagerStateListener.class);
         verify(mDreamManagerInternalMock).registerDreamManagerStateListener(
                 dreamManagerStateListener.capture());
-        dreamManagerStateListener.getValue().onKeepDreamingWhenUndockedChanged(false);
+        dreamManagerStateListener.getValue().onKeepDreamingWhenUnpluggingChanged(false);
 
         when(mBatteryManagerInternalMock.getPlugType())
                 .thenReturn(BatteryManager.BATTERY_PLUGGED_DOCK);
@@ -793,7 +793,7 @@
                 ArgumentCaptor.forClass(DreamManagerInternal.DreamManagerStateListener.class);
         verify(mDreamManagerInternalMock).registerDreamManagerStateListener(
                 dreamManagerStateListener.capture());
-        dreamManagerStateListener.getValue().onKeepDreamingWhenUndockedChanged(true);
+        dreamManagerStateListener.getValue().onKeepDreamingWhenUnpluggingChanged(true);
 
         when(mBatteryManagerInternalMock.getPlugType())
                 .thenReturn(BatteryManager.BATTERY_PLUGGED_DOCK);
@@ -823,7 +823,7 @@
                 ArgumentCaptor.forClass(DreamManagerInternal.DreamManagerStateListener.class);
         verify(mDreamManagerInternalMock).registerDreamManagerStateListener(
                 dreamManagerStateListener.capture());
-        dreamManagerStateListener.getValue().onKeepDreamingWhenUndockedChanged(true);
+        dreamManagerStateListener.getValue().onKeepDreamingWhenUnpluggingChanged(true);
 
         when(mBatteryManagerInternalMock.getPlugType())
                 .thenReturn(BatteryManager.BATTERY_PLUGGED_DOCK);
@@ -831,7 +831,7 @@
 
         forceAwake();  // Needs to be awake first before it can dream.
         forceDream();
-        dreamManagerStateListener.getValue().onKeepDreamingWhenUndockedChanged(false);
+        dreamManagerStateListener.getValue().onKeepDreamingWhenUnpluggingChanged(false);
         when(mBatteryManagerInternalMock.getPlugType()).thenReturn(0);
         setPluggedIn(false);
 
@@ -839,6 +839,34 @@
     }
 
     @Test
+    public void testWakefulnessDream_shouldStopDreamingWhenUnplugging_whenDreamPrevents() {
+        // Make sure "unplug turns on screen" is configured to true.
+        when(mResourcesSpy.getBoolean(com.android.internal.R.bool.config_unplugTurnsOnScreen))
+                .thenReturn(true);
+
+        createService();
+        startSystem();
+
+        ArgumentCaptor<DreamManagerInternal.DreamManagerStateListener> dreamManagerStateListener =
+                ArgumentCaptor.forClass(DreamManagerInternal.DreamManagerStateListener.class);
+        verify(mDreamManagerInternalMock).registerDreamManagerStateListener(
+                dreamManagerStateListener.capture());
+
+        when(mBatteryManagerInternalMock.getPlugType())
+                .thenReturn(BatteryManager.BATTERY_PLUGGED_AC);
+        setPluggedIn(true);
+
+        forceAwake();  // Needs to be awake first before it can dream.
+        forceDream();
+        dreamManagerStateListener.getValue().onKeepDreamingWhenUnpluggingChanged(false);
+        when(mBatteryManagerInternalMock.getPlugType()).thenReturn(0);
+        setPluggedIn(false);
+
+        assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+    }
+
+
+    @Test
     public void testWakefulnessDoze_goToSleep() {
         createService();
         // Start with AWAKE state
diff --git a/services/tests/servicestests/src/com/android/server/power/ShutdownCheckPointsTest.java b/services/tests/servicestests/src/com/android/server/power/ShutdownCheckPointsTest.java
index 2230ddd..2bde51b 100644
--- a/services/tests/servicestests/src/com/android/server/power/ShutdownCheckPointsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/ShutdownCheckPointsTest.java
@@ -103,21 +103,45 @@
     }
 
     @Test
-    public void testCallerProcessBinderEntry() throws RemoteException {
+    public void testCallerProcessBinderEntries() throws RemoteException {
         List<ActivityManager.RunningAppProcessInfo> runningAppProcessInfos = new ArrayList<>();
         runningAppProcessInfos.add(
                 new ActivityManager.RunningAppProcessInfo("process_name", 1, new String[0]));
         when(mActivityManager.getRunningAppProcesses()).thenReturn(runningAppProcessInfos);
 
         mTestInjector.setCurrentTime(1000);
+        // Matching pid in getRunningAppProcesses
+        mInstance.recordCheckPointInternal(1, "reason1");
+        // Mising pid in getRunningAppProcesses
+        mInstance.recordCheckPointInternal(2, "reason2");
+
+        assertEquals(
+                "Shutdown request from BINDER for reason reason1 "
+                        + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+                        + "com.android.server.power.ShutdownCheckPointsTest"
+                        + ".testCallerProcessBinderEntries\n"
+                        + "From process process_name (pid=1)\n\n"
+                        + "Shutdown request from BINDER for reason reason2 "
+                        + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+                        + "com.android.server.power.ShutdownCheckPointsTest"
+                        + ".testCallerProcessBinderEntries\n"
+                        + "From process ? (pid=2)\n\n",
+                dumpToString());
+    }
+
+    @Test
+    public void testNullCallerProcessBinderEntries() throws RemoteException {
+        when(mActivityManager.getRunningAppProcesses()).thenReturn(null);
+
+        mTestInjector.setCurrentTime(1000);
         mInstance.recordCheckPointInternal(1, "reason1");
 
         assertEquals(
                 "Shutdown request from BINDER for reason reason1 "
                         + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
                         + "com.android.server.power.ShutdownCheckPointsTest"
-                        + ".testCallerProcessBinderEntry\n"
-                        + "From process process_name (pid=1)\n\n",
+                        + ".testNullCallerProcessBinderEntries\n"
+                        + "From process ? (pid=1)\n\n",
                 dumpToString());
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java
similarity index 80%
rename from services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
rename to services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java
index 7cf5bc8..dca67d6 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java
@@ -14,13 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.server.power.stats;
+package com.android.server.power.stats.wakeups;
 
 import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM;
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER;
 import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
 import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI;
 
-import static com.android.server.power.stats.CpuWakeupStats.WAKEUP_REASON_HALF_WINDOW_MS;
+import static com.android.server.power.stats.wakeups.CpuWakeupStats.WAKEUP_REASON_HALF_WINDOW_MS;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -34,6 +35,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
 import com.android.frameworks.servicestests.R;
+import com.android.server.power.stats.wakeups.CpuWakeupStats.Wakeup;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -46,10 +48,11 @@
 @RunWith(AndroidJUnit4.class)
 public class CpuWakeupStatsTest {
     private static final String KERNEL_REASON_ALARM_IRQ = "120 test.alarm.device";
-    private static final String KERNEL_REASON_WIFI_IRQ = "120 test.wifi.device";
+    private static final String KERNEL_REASON_WIFI_IRQ = "130 test.wifi.device";
+    private static final String KERNEL_REASON_SOUND_TRIGGER_IRQ = "129 test.sound_trigger.device";
     private static final String KERNEL_REASON_UNKNOWN_IRQ = "140 test.unknown.device";
     private static final String KERNEL_REASON_UNKNOWN = "free-form-reason test.alarm.device";
-    private static final String KERNEL_REASON_UNSUPPORTED = "-1 test.alarm.device";
+    private static final String KERNEL_REASON_ALARM_ABNORMAL = "-1 test.alarm.device";
     private static final String KERNEL_REASON_ABORT = "Abort: due to test.alarm.device";
 
     private static final int TEST_UID_1 = 13239823;
@@ -140,6 +143,40 @@
     }
 
     @Test
+    public void soundTriggerIrqAttributionSolo() {
+        final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
+        final long wakeupTime = 1247121;
+
+        populateDefaultProcStates(obj);
+
+        obj.noteWakeupTimeAndReason(wakeupTime, 1, KERNEL_REASON_SOUND_TRIGGER_IRQ);
+
+        // Outside the window, so should be ignored.
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER,
+                wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1);
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER,
+                wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2);
+        // Should be attributed
+        obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER, wakeupTime + 5, TEST_UID_3,
+                TEST_UID_5);
+
+        final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+        assertThat(attribution).isNotNull();
+        assertThat(attribution.size()).isEqualTo(1);
+        assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER)).isTrue();
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER).indexOfKey(
+                TEST_UID_1)).isLessThan(0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER).indexOfKey(
+                TEST_UID_2)).isLessThan(0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER).get(TEST_UID_3)).isEqualTo(
+                TEST_PROC_STATE_3);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER).indexOfKey(
+                TEST_UID_4)).isLessThan(0);
+        assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER).get(TEST_UID_5)).isEqualTo(
+                TEST_PROC_STATE_5);
+    }
+
+    @Test
     public void wifiIrqAttributionSolo() {
         final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
         final long wakeupTime = 12423121;
@@ -268,22 +305,39 @@
     }
 
     @Test
-    public void unsupportedWakeupIgnored() {
+    public void abnormalAlarmAttribution() {
         final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
+        populateDefaultProcStates(obj);
 
         long wakeupTime = 970934;
-        obj.noteWakeupTimeAndReason(wakeupTime, 34, KERNEL_REASON_UNSUPPORTED);
+        obj.noteWakeupTimeAndReason(wakeupTime, 34, KERNEL_REASON_ALARM_ABNORMAL);
 
-        // Should be ignored as this type of wakeup is unsupported.
-        assertThat(obj.mWakeupEvents.size()).isEqualTo(0);
+        assertThat(obj.mWakeupEvents.size()).isEqualTo(1);
+        assertThat(obj.mWakeupEvents.valueAt(0).mType).isEqualTo(Wakeup.TYPE_ABNORMAL);
 
         obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
         obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 3, TEST_UID_4);
 
-        // Any nearby activity should not end up in the attribution map.
-        assertThat(obj.mWakeupAttribution.size()).isEqualTo(0);
+        final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+        assertThat(attribution.size()).isEqualTo(1);
+        assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_ALARM)).isTrue();
+        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).indexOfKey(TEST_UID_5)).isLessThan(
+                0);
+    }
 
-        wakeupTime = 883124;
+    @Test
+    public void abortIgnored() {
+        final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
+
+        long wakeupTime = 883124;
         obj.noteWakeupTimeAndReason(wakeupTime, 3, KERNEL_REASON_ABORT);
 
         // Should be ignored as this type of wakeup is unsupported.
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/IrqDeviceMapTest.java b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/IrqDeviceMapTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/power/stats/IrqDeviceMapTest.java
rename to services/tests/servicestests/src/com/android/server/power/stats/wakeups/IrqDeviceMapTest.java
index 43d9e60..47a8f49 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/IrqDeviceMapTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/IrqDeviceMapTest.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.power.stats;
+package com.android.server.power.stats.wakeups;
 
 import static com.google.common.truth.Truth.assertThat;
 
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerDuplicateModelHandlerTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerDuplicateModelHandlerTest.java
new file mode 100644
index 0000000..bc94dac
--- /dev/null
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerDuplicateModelHandlerTest.java
@@ -0,0 +1,335 @@
+/*
+ * 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.server.soundtrigger_middleware;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.Phrase;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionMode;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.SoundModelType;
+import android.media.soundtrigger.Status;
+import android.os.IBinder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.InOrder;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(JUnit4.class)
+public final class SoundTriggerDuplicateModelHandlerTest {
+    // Component under test
+    private SoundTriggerDuplicateModelHandler mComponent;
+
+    private static final String DUPLICATE_UUID = "abcddead-beef-0123-3210-0123456789ab";
+    private static final String DIFFERENT_UUID = "0000dead-beef-0123-3210-0123456789ab";
+
+    @Mock private ISoundTriggerHal mUnderlying;
+    @Mock private ISoundTriggerHal.GlobalCallback mGlobalCallback;
+    @Mock private ISoundTriggerHal.ModelCallback mModelCallback;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mComponent = new SoundTriggerDuplicateModelHandler(mUnderlying);
+        doNothing().when(mUnderlying).registerCallback(any());
+        mComponent.registerCallback(mGlobalCallback);
+        verify(mUnderlying).registerCallback(eq(mGlobalCallback));
+    }
+
+    @Test
+    public void loadSoundModel_throwsResourceContention_whenDuplicateUuid() {
+        final var soundModel = createSoundModelOne();
+        final var soundModelSameUuid = createSoundModelTwo();
+        // First sound model load should complete successfully
+        mComponent.loadSoundModel(soundModel, mModelCallback);
+        verify(mUnderlying).loadSoundModel(eq(soundModel), eq(mModelCallback));
+        assertEquals(
+                assertThrows(
+                                RecoverableException.class,
+                                () -> mComponent.loadSoundModel(soundModelSameUuid, mModelCallback))
+                        .errorCode,
+                Status.RESOURCE_CONTENTION);
+        // Model has not been unloaded, so we don't get a callback
+        verify(mGlobalCallback, never()).onResourcesAvailable();
+        verifyNoMoreInteractions(mUnderlying);
+        verifyNoMoreInteractions(mGlobalCallback);
+    }
+
+    @Test
+    public void loadSoundModel_doesNotThrowResourceContention_whenDifferentUuid() {
+        final var soundModel = createSoundModelOne();
+        // Make all other fields the same
+        final var soundModelDifferentUuid = createSoundModelOne();
+        soundModelDifferentUuid.uuid = DIFFERENT_UUID;
+        InOrder inOrder = Mockito.inOrder(mUnderlying);
+        // First sound model load should complete successfully
+        mComponent.loadSoundModel(soundModel, mModelCallback);
+        inOrder.verify(mUnderlying).loadSoundModel(eq(soundModel), eq(mModelCallback));
+        mComponent.loadSoundModel(soundModelDifferentUuid, mModelCallback);
+        inOrder.verify(mUnderlying).loadSoundModel(eq(soundModelDifferentUuid), eq(mModelCallback));
+        // No contention, so we don't get a callback
+        verify(mGlobalCallback, never()).onResourcesAvailable();
+        verifyNoMoreInteractions(mUnderlying);
+        verifyNoMoreInteractions(mGlobalCallback);
+    }
+
+    @Test
+    public void loadSoundModel_doesNotThrow_afterDuplicateUuidHasBeenUnloaded() {
+        final var soundModel = createSoundModelOne();
+        // First sound model load should complete successfully
+        int handle = mComponent.loadSoundModel(soundModel, mModelCallback);
+        verify(mUnderlying).loadSoundModel(eq(soundModel), eq(mModelCallback));
+        // Unload model should complete successfully
+        mComponent.unloadSoundModel(handle);
+        verify(mUnderlying).unloadSoundModel(eq(handle));
+        // Since the model with the same UUID was unloaded, the subsequent load model
+        // should succeed.
+        mComponent.loadSoundModel(soundModel, mModelCallback);
+        verify(mUnderlying, times(2)).loadSoundModel(eq(soundModel), eq(mModelCallback));
+        verifyNoMoreInteractions(mUnderlying);
+        verifyNoMoreInteractions(mGlobalCallback);
+    }
+
+    @Test
+    public void unloadSoundModel_triggersResourceCallback_afterDuplicateUuidRejected() {
+        final var soundModel = createSoundModelOne();
+        final var soundModelSameUuid = createSoundModelTwo();
+        // First sound model load should complete successfully
+        int handle = mComponent.loadSoundModel(soundModel, mModelCallback);
+        verify(mUnderlying).loadSoundModel(eq(soundModel), eq(mModelCallback));
+        assertEquals(
+                assertThrows(
+                                RecoverableException.class,
+                                () -> mComponent.loadSoundModel(soundModelSameUuid, mModelCallback))
+                        .errorCode,
+                Status.RESOURCE_CONTENTION);
+        mComponent.unloadSoundModel(handle);
+        verify(mUnderlying).unloadSoundModel(eq(handle));
+        verify(mGlobalCallback).onResourcesAvailable();
+        verifyNoMoreInteractions(mUnderlying);
+        verifyNoMoreInteractions(mGlobalCallback);
+    }
+
+    // Next tests are same as above, but for phrase sound model.
+    @Test
+    public void loadPhraseSoundModel_throwsResourceContention_whenDuplicateUuid() {
+        final var soundModel = createPhraseSoundModelOne();
+        final var soundModelSameUuid = createPhraseSoundModelTwo();
+        // First sound model load should complete successfully
+        mComponent.loadPhraseSoundModel(soundModel, mModelCallback);
+        verify(mUnderlying).loadPhraseSoundModel(eq(soundModel), eq(mModelCallback));
+        assertEquals(
+                assertThrows(
+                                RecoverableException.class,
+                                () ->
+                                        mComponent.loadPhraseSoundModel(
+                                                soundModelSameUuid, mModelCallback))
+                        .errorCode,
+                Status.RESOURCE_CONTENTION);
+        // Model has not been unloaded, so we don't get a callback
+        verify(mGlobalCallback, never()).onResourcesAvailable();
+        verifyNoMoreInteractions(mUnderlying);
+        verifyNoMoreInteractions(mGlobalCallback);
+    }
+
+    @Test
+    public void loadPhraseSoundModel_doesNotThrowResourceContention_whenDifferentUuid() {
+        final var soundModel = createPhraseSoundModelOne();
+        // Make all other fields the same
+        final var soundModelDifferentUuid = createPhraseSoundModelOne();
+        soundModelDifferentUuid.common.uuid = DIFFERENT_UUID;
+        InOrder inOrder = Mockito.inOrder(mUnderlying);
+        // First sound model load should complete successfully
+        mComponent.loadPhraseSoundModel(soundModel, mModelCallback);
+        inOrder.verify(mUnderlying).loadPhraseSoundModel(eq(soundModel), eq(mModelCallback));
+        mComponent.loadPhraseSoundModel(soundModelDifferentUuid, mModelCallback);
+        inOrder.verify(mUnderlying).loadPhraseSoundModel(eq(soundModelDifferentUuid),
+                eq(mModelCallback));
+        // No contention, so we don't get a callback
+        verify(mGlobalCallback, never()).onResourcesAvailable();
+        verifyNoMoreInteractions(mUnderlying);
+        verifyNoMoreInteractions(mGlobalCallback);
+    }
+
+    @Test
+    public void loadPhraseSoundModel_doesNotThrow_afterDuplicateUuidHasBeenUnloaded() {
+        final var soundModel = createPhraseSoundModelOne();
+        // First sound model load should complete successfully
+        int handle = mComponent.loadPhraseSoundModel(soundModel, mModelCallback);
+        verify(mUnderlying).loadPhraseSoundModel(eq(soundModel), eq(mModelCallback));
+        // Unload model should complete successfully
+        mComponent.unloadSoundModel(handle);
+        verify(mUnderlying).unloadSoundModel(eq(handle));
+        // Since the model with the same UUID was unloaded, the subsequent load model
+        // should succeed.
+        mComponent.loadPhraseSoundModel(soundModel, mModelCallback);
+        verify(mUnderlying, times(2)).loadPhraseSoundModel(eq(soundModel), eq(mModelCallback));
+        verifyNoMoreInteractions(mUnderlying);
+        verifyNoMoreInteractions(mGlobalCallback);
+    }
+
+    @Test
+    public void unloadSoundModel_triggersResourceCallback_afterDuplicateUuidRejectedPhrase() {
+        final var soundModel = createPhraseSoundModelOne();
+        final var soundModelSameUuid = createPhraseSoundModelTwo();
+        // First sound model load should complete successfully
+        int handle = mComponent.loadPhraseSoundModel(soundModel, mModelCallback);
+        verify(mUnderlying).loadPhraseSoundModel(eq(soundModel), eq(mModelCallback));
+        assertEquals(
+                assertThrows(
+                                RecoverableException.class,
+                                () ->
+                                        mComponent.loadPhraseSoundModel(
+                                                soundModelSameUuid, mModelCallback))
+                        .errorCode,
+                Status.RESOURCE_CONTENTION);
+        mComponent.unloadSoundModel(handle);
+        verify(mUnderlying).unloadSoundModel(eq(handle));
+        verify(mGlobalCallback).onResourcesAvailable();
+        verifyNoMoreInteractions(mUnderlying);
+        verifyNoMoreInteractions(mGlobalCallback);
+    }
+
+    @Test
+    public void testDelegation() {
+        // Test that the rest of the interface delegates its calls to the underlying object
+        // appropriately.
+        // This test method does not test load/unloadSoundModel
+        var properties = new Properties();
+        InOrder inOrder = Mockito.inOrder(mUnderlying);
+        doReturn(properties).when(mUnderlying).getProperties();
+        assertEquals(mComponent.getProperties(), properties);
+        inOrder.verify(mUnderlying).getProperties();
+        var mockGlobalCallback = mock(ISoundTriggerHal.GlobalCallback.class);
+        mComponent.registerCallback(mockGlobalCallback);
+        inOrder.verify(mUnderlying).registerCallback(eq(mockGlobalCallback));
+        int modelId = 5;
+        int deviceHandle = 2;
+        int ioHandle = 3;
+        var config = mock(RecognitionConfig.class);
+        mComponent.startRecognition(modelId, deviceHandle, ioHandle, config);
+        inOrder.verify(mUnderlying)
+                .startRecognition(eq(modelId), eq(deviceHandle), eq(ioHandle), eq(config));
+
+        mComponent.stopRecognition(modelId);
+        inOrder.verify(mUnderlying).stopRecognition(eq(modelId));
+        mComponent.forceRecognitionEvent(modelId);
+        inOrder.verify(mUnderlying).forceRecognitionEvent(eq(modelId));
+        int param = 10;
+        int value = 50;
+        var modelParamRange = new ModelParameterRange();
+        doReturn(modelParamRange).when(mUnderlying).queryParameter(anyInt(), anyInt());
+        assertEquals(mComponent.queryParameter(param, value), modelParamRange);
+        inOrder.verify(mUnderlying).queryParameter(param, value);
+        doReturn(value).when(mUnderlying).getModelParameter(anyInt(), anyInt());
+        assertEquals(mComponent.getModelParameter(modelId, param), value);
+        inOrder.verify(mUnderlying).getModelParameter(eq(modelId), eq(param));
+        mComponent.setModelParameter(modelId, param, value);
+        inOrder.verify(mUnderlying).setModelParameter(eq(modelId), eq(param), eq(value));
+        var recipient = mock(IBinder.DeathRecipient.class);
+        mComponent.linkToDeath(recipient);
+        inOrder.verify(mUnderlying).linkToDeath(eq(recipient));
+        mComponent.unlinkToDeath(recipient);
+        inOrder.verify(mUnderlying).unlinkToDeath(eq(recipient));
+        mComponent.flushCallbacks();
+        inOrder.verify(mUnderlying).flushCallbacks();
+        var token = mock(IBinder.class);
+        mComponent.clientAttached(token);
+        inOrder.verify(mUnderlying).clientAttached(eq(token));
+        mComponent.clientDetached(token);
+        inOrder.verify(mUnderlying).clientDetached(eq(token));
+        mComponent.reboot();
+        inOrder.verify(mUnderlying).reboot();
+        mComponent.detach();
+        inOrder.verify(mUnderlying).detach();
+        verifyNoMoreInteractions(mUnderlying);
+        verifyNoMoreInteractions(mGlobalCallback);
+    }
+
+    private static SoundModel createSoundModelOne() {
+        SoundModel model = new SoundModel();
+        model.type = SoundModelType.GENERIC;
+        model.uuid = DUPLICATE_UUID;
+        model.vendorUuid = "87654321-5432-6543-7654-456789fedcba";
+        byte[] data = new byte[] {91, 92, 93, 94, 95};
+        model.data = TestUtil.byteArrayToParcelFileDescriptor(data);
+        model.dataSize = data.length;
+        return model;
+    }
+
+    // Different except for the same UUID
+    private static SoundModel createSoundModelTwo() {
+        SoundModel model = new SoundModel();
+        model.type = SoundModelType.GENERIC;
+        model.uuid = DUPLICATE_UUID;
+        model.vendorUuid = "12345678-9876-5432-1012-345678901234";
+        byte[] data = new byte[] {19, 18, 17, 16};
+        model.data = TestUtil.byteArrayToParcelFileDescriptor(data);
+        model.dataSize = data.length;
+        return model;
+    }
+
+    private static PhraseSoundModel createPhraseSoundModelOne() {
+        PhraseSoundModel model = new PhraseSoundModel();
+        model.common = createSoundModelOne();
+        model.common.type = SoundModelType.KEYPHRASE;
+        model.phrases = new Phrase[1];
+        model.phrases[0] = new Phrase();
+        model.phrases[0].id = 123;
+        model.phrases[0].users = new int[] {5, 6, 7};
+        model.phrases[0].locale = "locale";
+        model.phrases[0].text = "text";
+        model.phrases[0].recognitionModes =
+                RecognitionMode.USER_AUTHENTICATION | RecognitionMode.USER_IDENTIFICATION;
+        return model;
+    }
+
+    private static PhraseSoundModel createPhraseSoundModelTwo() {
+        PhraseSoundModel model = new PhraseSoundModel();
+        model.common = createSoundModelTwo();
+        model.common.type = SoundModelType.KEYPHRASE;
+        model.phrases = new Phrase[1];
+        model.phrases[0] = new Phrase();
+        model.phrases[0].id = 321;
+        model.phrases[0].users = new int[] {4, 3, 2, 1};
+        model.phrases[0].locale = "differentLocale";
+        model.phrases[0].text = "differentText";
+        model.phrases[0].recognitionModes = 0;
+        return model;
+    }
+}
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/TestUtil.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/TestUtil.java
index 39561f7..3b7bc95 100644
--- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/TestUtil.java
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/TestUtil.java
@@ -466,7 +466,7 @@
         assertEquals(43, event.phraseExtras[0].levels[0].levelPercent);
     }
 
-    private static ParcelFileDescriptor byteArrayToParcelFileDescriptor(byte[] data) {
+    static ParcelFileDescriptor byteArrayToParcelFileDescriptor(byte[] data) {
         try (SharedMemory shmem = SharedMemory.create("", data.length)) {
             ByteBuffer buffer = shmem.mapReadWrite();
             buffer.put(data);
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 2696d2b..f12b53a 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -87,6 +87,8 @@
                   android:showWhenLocked="true"/>
         <activity android:name="android.view.cts.surfacevalidator.CapturedActivity"/>
 
+        <activity android:name="com.android.server.wm.SurfaceControlViewHostTests$TestActivity" />
+
         <service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService"
             android:foregroundServiceType="mediaProjection"
             android:enabled="true">
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
new file mode 100644
index 0000000..41bfc80
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
@@ -0,0 +1,194 @@
+/*
+ * 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.wm;
+
+import static android.Manifest.permission.ACCESS_SURFACE_FLINGER;
+import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
+import static android.server.wm.CtsWindowInfoUtils.waitForWindowVisible;
+import static android.server.wm.CtsWindowInfoUtils.waitForWindowFocus;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.view.Gravity;
+import android.view.IWindow;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+import android.view.WindowlessWindowManager;
+import android.widget.Button;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+
+@Presubmit
+@SmallTest
+@RunWith(WindowTestRunner.class)
+public class SurfaceControlViewHostTests {
+    private final ActivityTestRule<TestActivity> mActivityRule = new ActivityTestRule<>(
+            TestActivity.class);
+    private Instrumentation mInstrumentation;
+    private TestActivity mActivity;
+
+    private View mView1;
+    private View mView2;
+    private SurfaceControlViewHost mScvh1;
+    private SurfaceControlViewHost mScvh2;
+
+    @Before
+    public void setUp() throws Exception {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+
+        // ACCESS_SURFACE_FLINGER is necessary to call waitForWindow
+        // INTERNAL_SYSTEM_WINDOW is necessary to add SCVH with no host parent
+        mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(ACCESS_SURFACE_FLINGER,
+                INTERNAL_SYSTEM_WINDOW);
+        mActivity = mActivityRule.launchActivity(null);
+    }
+
+    @After
+    public void tearDown() {
+        mInstrumentation.getUiAutomation().dropShellPermissionIdentity();
+    }
+
+    @Test
+    public void requestFocusWithMultipleWindows() throws InterruptedException, RemoteException {
+        SurfaceControl sc = new SurfaceControl.Builder()
+                .setName("SurfaceControlViewHostTests")
+                .setCallsite("requestFocusWithMultipleWindows")
+                .build();
+        mView1 = new Button(mActivity);
+        mView2 = new Button(mActivity);
+
+        mActivity.runOnUiThread(() -> {
+            TestWindowlessWindowManager wwm = new TestWindowlessWindowManager(
+                    mActivity.getResources().getConfiguration(), sc, null);
+
+            try {
+                mActivity.attachToSurfaceView(sc);
+            } catch (InterruptedException e) {
+            }
+
+            mScvh1 = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(),
+                    wwm, "requestFocusWithMultipleWindows");
+            mScvh2 = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(),
+                    wwm, "requestFocusWithMultipleWindows");
+
+
+            mView1.setBackgroundColor(Color.RED);
+            mView2.setBackgroundColor(Color.BLUE);
+
+            WindowManager.LayoutParams lp1 = new WindowManager.LayoutParams(200, 200,
+                    TYPE_APPLICATION, 0, PixelFormat.OPAQUE);
+            WindowManager.LayoutParams lp2 = new WindowManager.LayoutParams(100, 100,
+                    TYPE_APPLICATION, 0, PixelFormat.OPAQUE);
+            mScvh1.setView(mView1, lp1);
+            mScvh2.setView(mView2, lp2);
+        });
+
+        assertTrue("Failed to wait for view1", waitForWindowVisible(mView1));
+        assertTrue("Failed to wait for view2", waitForWindowVisible(mView2));
+
+        WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
+                mScvh1.getFocusGrantToken(), true);
+        assertTrue("Failed to gain focus for view1", waitForWindowFocus(mView1, true));
+
+        WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
+                mScvh2.getFocusGrantToken(), true);
+        assertTrue("Failed to gain focus for view2", waitForWindowFocus(mView2, true));
+    }
+
+    private static class TestWindowlessWindowManager extends WindowlessWindowManager {
+        private final SurfaceControl mRoot;
+
+        TestWindowlessWindowManager(Configuration c, SurfaceControl rootSurface,
+                IBinder hostInputToken) {
+            super(c, rootSurface, hostInputToken);
+            mRoot = rootSurface;
+        }
+
+        @Override
+        protected SurfaceControl getParentSurface(IWindow window,
+                WindowManager.LayoutParams attrs) {
+            return mRoot;
+        }
+    }
+
+    public static class TestActivity extends Activity implements SurfaceHolder.Callback {
+        private SurfaceView mSurfaceView;
+        private final CountDownLatch mSvReadyLatch = new CountDownLatch(1);
+
+        @Override
+        protected void onCreate(@Nullable Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            final FrameLayout content = new FrameLayout(this);
+            mSurfaceView = new SurfaceView(this);
+            mSurfaceView.setBackgroundColor(Color.BLACK);
+            mSurfaceView.setZOrderOnTop(true);
+            final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(500, 500,
+                    Gravity.LEFT | Gravity.TOP);
+            content.addView(mSurfaceView, lp);
+            setContentView(content);
+            mSurfaceView.getHolder().addCallback(this);
+        }
+
+        @Override
+        public void surfaceCreated(@NonNull SurfaceHolder holder) {
+            mSvReadyLatch.countDown();
+        }
+
+        @Override
+        public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
+                int height) {
+        }
+
+        @Override
+        public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
+        }
+
+        public void attachToSurfaceView(SurfaceControl sc) throws InterruptedException {
+            mSvReadyLatch.await();
+            new SurfaceControl.Transaction().reparent(sc, mSurfaceView.getSurfaceControl())
+                    .show(sc).apply();
+        }
+    }
+}
+
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerDuplicateModelHandler.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerDuplicateModelHandler.java
new file mode 100644
index 0000000..0104193
--- /dev/null
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerDuplicateModelHandler.java
@@ -0,0 +1,205 @@
+/*
+ * 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_middleware;
+
+import android.annotation.NonNull;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
+import android.os.IBinder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This wrapper prevents a models with the same UUID from being loaded concurrently. This is used to
+ * protect STHAL implementations, which don't support concurrent loads of the same model. We reject
+ * the duplicate load with {@link Status#RESOURCE_CONTENTION}.
+ */
+public class SoundTriggerDuplicateModelHandler implements ISoundTriggerHal {
+    private final @NonNull ISoundTriggerHal mDelegate;
+
+    private GlobalCallback mGlobalCallback;
+    // There are rarely more than two models loaded.
+    private final List<ModelData> mModelList = new ArrayList<>();
+
+    private static final class ModelData {
+        ModelData(int modelId, String uuid) {
+            mModelId = modelId;
+            mUuid = uuid;
+        }
+
+        int getModelId() {
+            return mModelId;
+        }
+
+        String getUuid() {
+            return mUuid;
+        }
+
+        boolean getWasContended() {
+            return mWasContended;
+        }
+
+        void setWasContended() {
+            mWasContended = true;
+        }
+
+        private int mModelId;
+        private String mUuid;
+        private boolean mWasContended = false;
+    }
+
+    public SoundTriggerDuplicateModelHandler(@NonNull ISoundTriggerHal delegate) {
+        mDelegate = delegate;
+    }
+
+    @Override
+    public void reboot() {
+        mDelegate.reboot();
+    }
+
+    @Override
+    public void detach() {
+        mDelegate.detach();
+    }
+
+    @Override
+    public Properties getProperties() {
+        return mDelegate.getProperties();
+    }
+
+    @Override
+    public void registerCallback(GlobalCallback callback) {
+        mGlobalCallback = callback;
+        mDelegate.registerCallback(mGlobalCallback);
+    }
+
+    @Override
+    public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+        synchronized (this) {
+            checkDuplicateModelUuid(soundModel.uuid);
+            var result = mDelegate.loadSoundModel(soundModel, callback);
+            mModelList.add(new ModelData(result, soundModel.uuid));
+            return result;
+        }
+    }
+
+    @Override
+    public int loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback) {
+        synchronized (this) {
+            checkDuplicateModelUuid(soundModel.common.uuid);
+            var result = mDelegate.loadPhraseSoundModel(soundModel, callback);
+            mModelList.add(new ModelData(result, soundModel.common.uuid));
+            return result;
+        }
+    }
+
+    @Override
+    public void unloadSoundModel(int modelHandle) {
+        mDelegate.unloadSoundModel(modelHandle);
+        for (int i = 0; i < mModelList.size(); i++) {
+            if (mModelList.get(i).getModelId() == modelHandle) {
+                var modelData = mModelList.remove(i);
+                if (modelData.getWasContended()) {
+                    mGlobalCallback.onResourcesAvailable();
+                }
+                // Model ID is unique
+                return;
+            }
+        }
+    }
+
+    // Uninteresting delegation calls to follow.
+    @Override
+    public void stopRecognition(int modelHandle) {
+        mDelegate.stopRecognition(modelHandle);
+    }
+
+    @Override
+    public void startRecognition(
+            int modelHandle, int deviceHandle, int ioHandle, RecognitionConfig config) {
+        mDelegate.startRecognition(modelHandle, deviceHandle, ioHandle, config);
+    }
+
+    @Override
+    public void forceRecognitionEvent(int modelHandle) {
+        mDelegate.forceRecognitionEvent(modelHandle);
+    }
+
+    @Override
+    public int getModelParameter(int modelHandle, int param) {
+        return mDelegate.getModelParameter(modelHandle, param);
+    }
+
+    @Override
+    public void setModelParameter(int modelHandle, int param, int value) {
+        mDelegate.setModelParameter(modelHandle, param, value);
+    }
+
+    @Override
+    public ModelParameterRange queryParameter(int modelHandle, int param) {
+        return mDelegate.queryParameter(modelHandle, param);
+    }
+
+    @Override
+    public void linkToDeath(IBinder.DeathRecipient recipient) {
+        mDelegate.linkToDeath(recipient);
+    }
+
+    @Override
+    public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+        mDelegate.unlinkToDeath(recipient);
+    }
+
+    @Override
+    public String interfaceDescriptor() {
+        return mDelegate.interfaceDescriptor();
+    }
+
+    @Override
+    public void flushCallbacks() {
+        mDelegate.flushCallbacks();
+    }
+
+    @Override
+    public void clientAttached(IBinder binder) {
+        mDelegate.clientAttached(binder);
+    }
+
+    @Override
+    public void clientDetached(IBinder binder) {
+        mDelegate.clientDetached(binder);
+    }
+
+    /**
+     * Helper for handling duplicate model. If there is a load attempt for a model with a UUID which
+     * is already loaded: 1) Reject with {@link Status.RESOURCE_CONTENTION} 2) Mark the already
+     * loaded model as contended, as we need to dispatch a resource available callback following the
+     * original model being unloaded.
+     */
+    private void checkDuplicateModelUuid(String uuid) {
+        var model = mModelList.stream().filter(x -> x.getUuid().equals(uuid)).findFirst();
+        if (model.isPresent()) {
+            model.get().setWasContended();
+            throw new RecoverableException(Status.RESOURCE_CONTENTION);
+        }
+    }
+}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index d2d8f1a..6223b2e 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -170,7 +170,8 @@
      */
     private void attachToHal() {
         mHalService = new SoundTriggerHalEnforcer(
-                new SoundTriggerHalWatchdog(mHalFactory.create()));
+                new SoundTriggerHalWatchdog(
+                    new SoundTriggerDuplicateModelHandler(mHalFactory.create())));
         mHalService.linkToDeath(this);
         mHalService.registerCallback(this);
         mProperties = mHalService.getProperties();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index a1adee7..486945d 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -154,7 +154,8 @@
     private int mRestartCount = 0;
     @NonNull private ServiceConnection mRemoteHotwordDetectionService;
     @NonNull private ServiceConnection mRemoteVisualQueryDetectionService;
-    private IBinder mAudioFlinger;
+    @GuardedBy("mLock")
+    @Nullable private IBinder mAudioFlinger;
     @GuardedBy("mLock")
     private boolean mDebugHotwordLogging = false;
 
@@ -194,7 +195,7 @@
                 new Intent(VisualQueryDetectionService.SERVICE_INTERFACE);
         visualQueryDetectionServiceIntent.setComponent(mVisualQueryDetectionComponentName);
 
-        initAudioFlingerLocked();
+        initAudioFlinger();
 
         mHotwordDetectionServiceConnectionFactory =
                 new ServiceConnectionFactory(hotwordDetectionServiceIntent,
@@ -227,31 +228,41 @@
         }
     }
 
-    private void initAudioFlingerLocked() {
+    private void initAudioFlinger() {
         if (DEBUG) {
-            Slog.d(TAG, "initAudioFlingerLocked");
+            Slog.d(TAG, "initAudioFlinger");
         }
-        mAudioFlinger = ServiceManager.waitForService("media.audio_flinger");
-        if (mAudioFlinger == null) {
+        final IBinder audioFlinger = ServiceManager.waitForService("media.audio_flinger");
+        if (audioFlinger == null) {
+            setAudioFlinger(null);
             throw new IllegalStateException("Service media.audio_flinger wasn't found.");
         }
         if (DEBUG) {
             Slog.d(TAG, "Obtained audio_flinger binder.");
         }
         try {
-            mAudioFlinger.linkToDeath(mAudioServerDeathRecipient, /* flags= */ 0);
+            audioFlinger.linkToDeath(mAudioServerDeathRecipient, /* flags= */ 0);
         } catch (RemoteException e) {
             Slog.w(TAG, "Audio server died before we registered a DeathRecipient; "
-                            + "retrying init.", e);
-            initAudioFlingerLocked();
+                    + "retrying init.", e);
+            initAudioFlinger();
+            return;
+        }
+
+        setAudioFlinger(audioFlinger);
+    }
+
+    private void setAudioFlinger(@Nullable IBinder audioFlinger) {
+        synchronized (mLock) {
+            mAudioFlinger = audioFlinger;
         }
     }
 
     private void audioServerDied() {
         Slog.w(TAG, "Audio server died; restarting the HotwordDetectionService.");
+        // TODO: Check if this needs to be scheduled on a different thread.
+        initAudioFlinger();
         synchronized (mLock) {
-            // TODO: Check if this needs to be scheduled on a different thread.
-            initAudioFlingerLocked();
             // We restart the process instead of simply sending over the new binder, to avoid race
             // conditions with audio reading in the service.
             restartProcessLocked();
diff --git a/telephony/java/android/telephony/satellite/PointingInfo.java b/telephony/java/android/telephony/satellite/PointingInfo.java
index e8a8576..a559b32 100644
--- a/telephony/java/android/telephony/satellite/PointingInfo.java
+++ b/telephony/java/android/telephony/satellite/PointingInfo.java
@@ -17,7 +17,6 @@
 package android.telephony.satellite;
 
 import android.annotation.NonNull;
-import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -34,7 +33,7 @@
     /**
      * @hide
      */
-    @UnsupportedAppUsage
+
     public PointingInfo(float satelliteAzimuthDegrees, float satelliteElevationDegrees) {
         mSatelliteAzimuthDegrees = satelliteAzimuthDegrees;
         mSatelliteElevationDegrees = satelliteElevationDegrees;
diff --git a/telephony/java/android/telephony/satellite/SatelliteCapabilities.java b/telephony/java/android/telephony/satellite/SatelliteCapabilities.java
index 0092890..6856cc0 100644
--- a/telephony/java/android/telephony/satellite/SatelliteCapabilities.java
+++ b/telephony/java/android/telephony/satellite/SatelliteCapabilities.java
@@ -17,7 +17,6 @@
 package android.telephony.satellite;
 
 import android.annotation.NonNull;
-import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -57,7 +56,6 @@
     /**
      * @hide
      */
-    @UnsupportedAppUsage
     public SatelliteCapabilities(Set<Integer> supportedRadioTechnologies,
             boolean isPointingRequired, int maxBytesPerOutgoingDatagram,
             @NonNull Map<Integer, AntennaPosition> antennaPositionMap) {
diff --git a/telephony/java/android/telephony/satellite/SatelliteDatagram.java b/telephony/java/android/telephony/satellite/SatelliteDatagram.java
index 44f56f3..d3cb8a0 100644
--- a/telephony/java/android/telephony/satellite/SatelliteDatagram.java
+++ b/telephony/java/android/telephony/satellite/SatelliteDatagram.java
@@ -17,7 +17,6 @@
 package android.telephony.satellite;
 
 import android.annotation.NonNull;
-import android.compat.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -33,7 +32,6 @@
     /**
      * @hide
      */
-    @UnsupportedAppUsage
     public SatelliteDatagram(@NonNull byte[] data) {
         mData = data;
     }
diff --git a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
index d0409bf..b2dec71 100644
--- a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
@@ -17,7 +17,6 @@
 package android.telephony.satellite;
 
 import android.annotation.NonNull;
-import android.compat.annotation.UnsupportedAppUsage;
 
 import java.util.function.Consumer;
 
@@ -37,7 +36,6 @@
      *                 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 Consumer<Void> callback);
 }
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 5681ab2..c5830b8 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -23,7 +23,6 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresFeature;
 import android.annotation.RequiresPermission;
-import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.Binder;
@@ -37,8 +36,8 @@
 import android.telephony.TelephonyFrameworkInitializer;
 
 import com.android.internal.telephony.IIntegerConsumer;
-import com.android.internal.telephony.IVoidConsumer;
 import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.IVoidConsumer;
 import com.android.telephony.Rlog;
 
 import java.lang.annotation.Retention;
@@ -83,7 +82,7 @@
      * @param context The context the SatelliteManager belongs to.
      * @hide
      */
-    @UnsupportedAppUsage
+
     public SatelliteManager(@Nullable Context context) {
         this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
     }
@@ -129,7 +128,7 @@
      * {@link #requestIsSatelliteEnabled(Executor, OutcomeReceiver)}.
      * @hide
      */
-    @UnsupportedAppUsage
+
     public static final String KEY_SATELLITE_ENABLED = "satellite_enabled";
 
     /**
@@ -137,7 +136,7 @@
      * {@link #requestIsDemoModeEnabled(Executor, OutcomeReceiver)}.
      * @hide
      */
-    @UnsupportedAppUsage
+
     public static final String KEY_DEMO_MODE_ENABLED = "demo_mode_enabled";
 
     /**
@@ -145,7 +144,7 @@
      * {@link #requestIsSatelliteSupported(Executor, OutcomeReceiver)}.
      * @hide
      */
-    @UnsupportedAppUsage
+
     public static final String KEY_SATELLITE_SUPPORTED = "satellite_supported";
 
     /**
@@ -153,7 +152,7 @@
      * {@link #requestSatelliteCapabilities(Executor, OutcomeReceiver)}.
      * @hide
      */
-    @UnsupportedAppUsage
+
     public static final String KEY_SATELLITE_CAPABILITIES = "satellite_capabilities";
 
     /**
@@ -161,7 +160,7 @@
      * {@link #requestIsSatelliteProvisioned(Executor, OutcomeReceiver)}.
      * @hide
      */
-    @UnsupportedAppUsage
+
     public static final String KEY_SATELLITE_PROVISIONED = "satellite_provisioned";
 
     /**
@@ -169,7 +168,7 @@
      * {@link #requestIsSatelliteCommunicationAllowedForCurrentLocation(Executor, OutcomeReceiver)}.
      * @hide
      */
-    @UnsupportedAppUsage
+
     public static final String KEY_SATELLITE_COMMUNICATION_ALLOWED =
             "satellite_communication_allowed";
 
@@ -178,7 +177,7 @@
      * {@link #requestTimeForNextSatelliteVisibility(Executor, OutcomeReceiver)}.
      * @hide
      */
-    @UnsupportedAppUsage
+
     public static final String KEY_SATELLITE_NEXT_VISIBILITY = "satellite_next_visibility";
 
     /**
@@ -389,7 +388,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @UnsupportedAppUsage
+
     public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode,
             @NonNull @CallbackExecutor Executor executor,
             @SatelliteError @NonNull Consumer<Integer> resultListener) {
@@ -432,7 +431,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @UnsupportedAppUsage
+
     public void requestIsSatelliteEnabled(@NonNull @CallbackExecutor Executor executor,
             @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
         Objects.requireNonNull(executor);
@@ -487,7 +486,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @UnsupportedAppUsage
+
     public void requestIsDemoModeEnabled(@NonNull @CallbackExecutor Executor executor,
             @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
         Objects.requireNonNull(executor);
@@ -540,7 +539,7 @@
      *
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
-    @UnsupportedAppUsage
+
     public void requestIsSatelliteSupported(@NonNull @CallbackExecutor Executor executor,
             @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
         Objects.requireNonNull(executor);
@@ -594,7 +593,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @UnsupportedAppUsage
+
     public void requestSatelliteCapabilities(@NonNull @CallbackExecutor Executor executor,
             @NonNull OutcomeReceiver<SatelliteCapabilities, SatelliteException> callback) {
         Objects.requireNonNull(executor);
@@ -787,7 +786,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @UnsupportedAppUsage
+
     public void startSatelliteTransmissionUpdates(@NonNull @CallbackExecutor Executor executor,
             @SatelliteError @NonNull Consumer<Integer> resultListener,
             @NonNull SatelliteTransmissionUpdateCallback callback) {
@@ -857,7 +856,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @UnsupportedAppUsage
+
     public void stopSatelliteTransmissionUpdates(
             @NonNull SatelliteTransmissionUpdateCallback callback,
             @NonNull @CallbackExecutor Executor executor,
@@ -913,7 +912,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @UnsupportedAppUsage
+
     public void provisionSatelliteService(@NonNull String token, @NonNull byte[] provisionData,
             @Nullable CancellationSignal cancellationSignal,
             @NonNull @CallbackExecutor Executor executor,
@@ -963,7 +962,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @UnsupportedAppUsage
+
     public void deprovisionSatelliteService(@NonNull String token,
             @NonNull @CallbackExecutor Executor executor,
             @SatelliteError @NonNull Consumer<Integer> resultListener) {
@@ -1003,7 +1002,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @UnsupportedAppUsage
+
     @SatelliteError public int registerForSatelliteProvisionStateChanged(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull SatelliteProvisionStateCallback callback) {
@@ -1046,7 +1045,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @UnsupportedAppUsage
+
     public void unregisterForSatelliteProvisionStateChanged(
             @NonNull SatelliteProvisionStateCallback callback) {
         Objects.requireNonNull(callback);
@@ -1085,7 +1084,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @UnsupportedAppUsage
+
     public void requestIsSatelliteProvisioned(@NonNull @CallbackExecutor Executor executor,
             @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
         Objects.requireNonNull(executor);
@@ -1137,7 +1136,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @UnsupportedAppUsage
+
     @SatelliteError public int registerForSatelliteModemStateChanged(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull SatelliteStateCallback callback) {
@@ -1177,7 +1176,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @UnsupportedAppUsage
+
     public void unregisterForSatelliteModemStateChanged(@NonNull SatelliteStateCallback callback) {
         Objects.requireNonNull(callback);
         ISatelliteStateCallback internalCallback = sSatelliteStateCallbackMap.remove(callback);
@@ -1212,7 +1211,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @UnsupportedAppUsage
+
     @SatelliteError public int registerForSatelliteDatagram(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull SatelliteDatagramCallback callback) {
@@ -1268,7 +1267,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @UnsupportedAppUsage
+
     public void unregisterForSatelliteDatagram(@NonNull SatelliteDatagramCallback callback) {
         Objects.requireNonNull(callback);
         ISatelliteDatagramCallback internalCallback =
@@ -1306,7 +1305,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @UnsupportedAppUsage
+
     public void pollPendingSatelliteDatagrams(@NonNull @CallbackExecutor Executor executor,
             @SatelliteError @NonNull Consumer<Integer> resultListener) {
         Objects.requireNonNull(executor);
@@ -1359,7 +1358,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @UnsupportedAppUsage
+
     public void sendSatelliteDatagram(@DatagramType int datagramType,
             @NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI,
             @NonNull @CallbackExecutor Executor executor,
@@ -1405,7 +1404,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @UnsupportedAppUsage
+
     public void requestIsSatelliteCommunicationAllowedForCurrentLocation(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
@@ -1463,7 +1462,7 @@
      * @throws IllegalStateException if the Telephony process is not currently available.
      */
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
-    @UnsupportedAppUsage
+
     public void requestTimeForNextSatelliteVisibility(@NonNull @CallbackExecutor Executor executor,
             @NonNull OutcomeReceiver<Duration, SatelliteException> callback) {
         Objects.requireNonNull(executor);
diff --git a/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
index 20875af..a62eb8b 100644
--- a/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
@@ -16,8 +16,6 @@
 
 package android.telephony.satellite;
 
-import android.compat.annotation.UnsupportedAppUsage;
-
 /**
  * A callback class for monitoring satellite provision state change events.
  *
@@ -30,6 +28,5 @@
      * @param provisioned The new provision state. {@code true} means satellite is provisioned
      *                    {@code false} means satellite is not provisioned.
      */
-    @UnsupportedAppUsage
     void onSatelliteProvisionStateChanged(boolean provisioned);
 }
diff --git a/telephony/java/android/telephony/satellite/SatelliteStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteStateCallback.java
index 3312e3a..d9ecaa3 100644
--- a/telephony/java/android/telephony/satellite/SatelliteStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteStateCallback.java
@@ -16,8 +16,6 @@
 
 package android.telephony.satellite;
 
-import android.compat.annotation.UnsupportedAppUsage;
-
 /**
  * A callback class for monitoring satellite modem state change events.
  *
@@ -28,6 +26,5 @@
      * Called when satellite modem state changes.
      * @param state The new satellite modem state.
      */
-    @UnsupportedAppUsage
     void onSatelliteModemStateChanged(@SatelliteManager.SatelliteModemState int state);
 }
diff --git a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
index 17fff44..d4fe57a 100644
--- a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
@@ -17,7 +17,6 @@
 package android.telephony.satellite;
 
 import android.annotation.NonNull;
-import android.compat.annotation.UnsupportedAppUsage;
 
 /**
  * A callback class for monitoring satellite position update and datagram transfer state change
@@ -31,7 +30,6 @@
      *
      * @param pointingInfo The pointing info containing the satellite location.
      */
-    @UnsupportedAppUsage
     void onSatellitePositionChanged(@NonNull PointingInfo pointingInfo);
 
     /**
@@ -41,7 +39,6 @@
      * @param sendPendingCount The number of datagrams that are currently being sent.
      * @param errorCode If datagram transfer failed, the reason for failure.
      */
-    @UnsupportedAppUsage
     void onSendDatagramStateChanged(
             @SatelliteManager.SatelliteDatagramTransferState int state, int sendPendingCount,
             @SatelliteManager.SatelliteError int errorCode);
@@ -53,7 +50,6 @@
      * @param receivePendingCount The number of datagrams that are currently pending to be received.
      * @param errorCode If datagram transfer failed, the reason for failure.
      */
-    @UnsupportedAppUsage
     void onReceiveDatagramStateChanged(
             @SatelliteManager.SatelliteDatagramTransferState int state, int receivePendingCount,
             @SatelliteManager.SatelliteError int errorCode);
diff --git a/tests/HandwritingIme/src/com/google/android/test/handwritingime/BoundsInfoDrawHelper.java b/tests/HandwritingIme/src/com/google/android/test/handwritingime/BoundsInfoDrawHelper.java
new file mode 100644
index 0000000..6b924f3
--- /dev/null
+++ b/tests/HandwritingIme/src/com/google/android/test/handwritingime/BoundsInfoDrawHelper.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.google.android.test.handwritingime;
+
+import static com.google.android.test.handwritingime.HandwritingIme.BOUNDS_INFO_EDITOR_BOUNDS;
+import static com.google.android.test.handwritingime.HandwritingIme.BOUNDS_INFO_NONE;
+import static com.google.android.test.handwritingime.HandwritingIme.BOUNDS_INFO_VISIBLE_LINE_BOUNDS;
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.view.View;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.EditorBoundsInfo;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.graphics.ColorUtils;
+
+import java.util.List;
+
+public class BoundsInfoDrawHelper {
+    private static final Paint sPaint = new Paint();
+    private static final int EDITOR_BOUNDS_COLOR =
+            ColorUtils.setAlphaComponent(Color.DKGRAY, 128);
+    private static final int HANDWRITING_BOUNDS_COLOR =
+            ColorUtils.setAlphaComponent(Color.BLUE, 128);
+    private static final int VISIBLE_LINE_BOUNDS_COLOR =
+            ColorUtils.setAlphaComponent(Color.MAGENTA, 128);
+
+    public static void draw(Canvas canvas, View inkView, int boundsInfoMode,
+            CursorAnchorInfo cursorAnchorInfo) {
+        if (boundsInfoMode == BOUNDS_INFO_NONE || cursorAnchorInfo == null) {
+            return;
+        }
+
+        // The matrix in CursorAnchorInfo transforms the editor coordinates to on-screen
+        // coordinates. We then transform the matrix from the on-screen coordinates to the
+        // inkView's coordinates. So the result matrix transforms the editor coordinates
+        // to the inkView coordinates.
+        final Matrix matrix = cursorAnchorInfo.getMatrix();
+        inkView.transformMatrixToLocal(matrix);
+
+        if ((boundsInfoMode & BOUNDS_INFO_EDITOR_BOUNDS) != 0) {
+            drawEditorBoundsInfo(canvas, matrix, cursorAnchorInfo.getEditorBoundsInfo());
+        }
+
+        if ((boundsInfoMode & BOUNDS_INFO_VISIBLE_LINE_BOUNDS) != 0) {
+            drawVisibleLineBounds(canvas, matrix, cursorAnchorInfo.getVisibleLineBounds());
+        }
+    }
+
+    private static void setPaintForEditorBoundsInfo() {
+        sPaint.reset();
+        sPaint.setStyle(Paint.Style.STROKE);
+        sPaint.setStrokeWidth(5f);
+    }
+
+    private static void drawEditorBoundsInfo(Canvas canvas, Matrix matrix,
+            @Nullable EditorBoundsInfo editorBoundsInfo) {
+        if (editorBoundsInfo == null) {
+            return;
+        }
+        final RectF editorBounds = editorBoundsInfo.getEditorBounds();
+        setPaintForEditorBoundsInfo();
+        if (editorBounds != null) {
+            final RectF localEditorBounds = new RectF(editorBounds);
+            matrix.mapRect(localEditorBounds);
+            sPaint.setColor(EDITOR_BOUNDS_COLOR);
+            canvas.drawRect(localEditorBounds, sPaint);
+        }
+
+        final RectF handwritingBounds = editorBoundsInfo.getHandwritingBounds();
+        if (handwritingBounds != null) {
+            final RectF localHandwritingBounds = new RectF(handwritingBounds);
+            matrix.mapRect(localHandwritingBounds);
+            sPaint.setColor(HANDWRITING_BOUNDS_COLOR);
+            canvas.drawRect(localHandwritingBounds, sPaint);
+        }
+    }
+
+    private static void setPaintForVisibleLineBounds() {
+        sPaint.reset();
+        sPaint.setStyle(Paint.Style.STROKE);
+        sPaint.setStrokeWidth(2f);
+        sPaint.setColor(VISIBLE_LINE_BOUNDS_COLOR);
+    }
+
+    private static void drawVisibleLineBounds(Canvas canvas, Matrix matrix,
+            List<RectF> visibleLineBounds) {
+        if (visibleLineBounds.isEmpty()) {
+            return;
+        }
+        setPaintForVisibleLineBounds();
+        for (RectF lineBound : visibleLineBounds) {
+            matrix.mapRect(lineBound);
+            canvas.drawRect(lineBound, sPaint);
+        }
+    }
+}
diff --git a/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java b/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java
index 2fd2368..8380dcf 100644
--- a/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java
+++ b/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java
@@ -25,7 +25,9 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
+import android.view.inputmethod.CursorAnchorInfo;
 import android.view.inputmethod.DeleteGesture;
+import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.HandwritingGesture;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InsertGesture;
@@ -34,6 +36,7 @@
 import android.view.inputmethod.SelectGesture;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 import android.widget.Spinner;
@@ -43,9 +46,6 @@
 import java.util.function.IntConsumer;
 
 public class HandwritingIme extends InputMethodService {
-
-    public static final int HEIGHT_DP = 100;
-
     private static final int OP_NONE = 0;
     private static final int OP_SELECT = 1;
     private static final int OP_DELETE = 2;
@@ -62,6 +62,12 @@
     private Spinner mRichGestureGranularitySpinner;
     private PointF mRichGestureStartPoint;
 
+    static final int BOUNDS_INFO_NONE = 0;
+    static final int BOUNDS_INFO_VISIBLE_LINE_BOUNDS = 1;
+    static final int BOUNDS_INFO_EDITOR_BOUNDS = 2;
+    private int mBoundsInfoMode = BOUNDS_INFO_NONE;
+    private LinearLayout mBoundsInfoCheckBoxes;
+
     private final IntConsumer mResultConsumer = value -> Log.d(TAG, "Gesture result: " + value);
 
     interface HandwritingFinisher {
@@ -201,12 +207,7 @@
     public View onCreateInputView() {
         Log.d(TAG, "onCreateInputView");
         final ViewGroup view = new FrameLayout(this);
-        final View inner = new View(this);
-        final float density = getResources().getDisplayMetrics().density;
-        final int height = (int) (HEIGHT_DP * density);
         view.setPadding(0, 0, 0, 0);
-        view.addView(inner, new FrameLayout.LayoutParams(
-                FrameLayout.LayoutParams.MATCH_PARENT, height));
 
         LinearLayout layout = new LinearLayout(this);
         layout.setLayoutParams(new LinearLayout.LayoutParams(
@@ -214,9 +215,9 @@
         layout.setOrientation(LinearLayout.VERTICAL);
         layout.addView(getRichGestureActionsSpinner());
         layout.addView(getRichGestureGranularitySpinner());
-
+        layout.addView(getBoundsInfoCheckBoxes());
+        layout.setBackgroundColor(getColor(R.color.holo_green_light));
         view.addView(layout);
-        inner.setBackgroundColor(getColor(R.color.holo_green_light));
 
         return view;
     }
@@ -228,7 +229,7 @@
         mRichGestureModeSpinner = new Spinner(this);
         mRichGestureModeSpinner.setPadding(100, 0, 100, 0);
         mRichGestureModeSpinner.setTooltipText("Handwriting IME mode");
-        String[] items = new String[] {
+        String[] items = new String[]{
                 "Handwriting IME - Rich gesture disabled",
                 "Rich gesture SELECT",
                 "Rich gesture DELETE",
@@ -259,6 +260,69 @@
         return mRichGestureModeSpinner;
     }
 
+    private void updateCursorAnchorInfo(int boundsInfoMode) {
+        final InputConnection ic = getCurrentInputConnection();
+        if (ic == null) return;
+
+        if (boundsInfoMode == BOUNDS_INFO_NONE) {
+            ic.requestCursorUpdates(0);
+            return;
+        }
+
+        final int cursorUpdateMode = InputConnection.CURSOR_UPDATE_MONITOR;
+        int cursorUpdateFilter = 0;
+        if ((boundsInfoMode & BOUNDS_INFO_EDITOR_BOUNDS) != 0) {
+            cursorUpdateFilter |= InputConnection.CURSOR_UPDATE_FILTER_EDITOR_BOUNDS;
+        }
+
+        if ((boundsInfoMode & BOUNDS_INFO_VISIBLE_LINE_BOUNDS) != 0) {
+            cursorUpdateFilter |= InputConnection.CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS;
+        }
+        ic.requestCursorUpdates(cursorUpdateMode | cursorUpdateFilter);
+    }
+
+    private void updateBoundsInfoMode() {
+        if (mInk != null) {
+            mInk.setBoundsInfoMode(mBoundsInfoMode);
+        }
+        updateCursorAnchorInfo(mBoundsInfoMode);
+    }
+
+    private View getBoundsInfoCheckBoxes() {
+        if (mBoundsInfoCheckBoxes != null) {
+            return mBoundsInfoCheckBoxes;
+        }
+        mBoundsInfoCheckBoxes = new LinearLayout(this);
+        mBoundsInfoCheckBoxes.setPadding(100, 0, 100, 0);
+        mBoundsInfoCheckBoxes.setOrientation(LinearLayout.HORIZONTAL);
+
+        final CheckBox editorBoundsInfoCheckBox = new CheckBox(this);
+        editorBoundsInfoCheckBox.setText("EditorBoundsInfo");
+        editorBoundsInfoCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
+            if (isChecked) {
+                mBoundsInfoMode |= BOUNDS_INFO_EDITOR_BOUNDS;
+            } else {
+                mBoundsInfoMode &= ~BOUNDS_INFO_EDITOR_BOUNDS;
+            }
+            updateBoundsInfoMode();
+        });
+
+        final CheckBox visibleLineBoundsInfoCheckBox = new CheckBox(this);
+        visibleLineBoundsInfoCheckBox.setText("VisibleLineBounds");
+        visibleLineBoundsInfoCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
+            if (isChecked) {
+                mBoundsInfoMode |= BOUNDS_INFO_VISIBLE_LINE_BOUNDS;
+            } else {
+                mBoundsInfoMode &= ~BOUNDS_INFO_VISIBLE_LINE_BOUNDS;
+            }
+            updateBoundsInfoMode();
+        });
+
+        mBoundsInfoCheckBoxes.addView(editorBoundsInfoCheckBox);
+        mBoundsInfoCheckBoxes.addView(visibleLineBoundsInfoCheckBox);
+        return mBoundsInfoCheckBoxes;
+    }
+
     private View getRichGestureGranularitySpinner() {
         if (mRichGestureGranularitySpinner != null) {
             return mRichGestureGranularitySpinner;
@@ -294,6 +358,7 @@
         Log.d(TAG, "onPrepareStylusHandwriting ");
         if (mInk == null) {
             mInk = new InkView(this, new HandwritingFinisherImpl(), new StylusConsumer());
+            mInk.setBoundsInfoMode(mBoundsInfoMode);
         }
     }
 
@@ -323,4 +388,16 @@
     private boolean areRichGesturesEnabled() {
         return mRichGestureMode != OP_NONE;
     }
+
+    @Override
+    public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
+        if (mInk != null) {
+            mInk.setCursorAnchorInfo(cursorAnchorInfo);
+        }
+    }
+
+    @Override
+    public void onStartInput(EditorInfo attribute, boolean restarting) {
+        updateCursorAnchorInfo(mBoundsInfoMode);
+    }
 }
diff --git a/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java b/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java
index e94c79e..86b324c 100644
--- a/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java
+++ b/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java
@@ -26,6 +26,7 @@
 import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.view.WindowMetrics;
+import android.view.inputmethod.CursorAnchorInfo;
 
 class InkView extends View {
     private static final long FINISH_TIMEOUT = 1500;
@@ -37,6 +38,9 @@
     private static final float STYLUS_MOVE_TOLERANCE = 1;
     private Runnable mFinishRunnable;
 
+    private CursorAnchorInfo mCursorAnchorInfo;
+    private int mBoundsInfoMode;
+
     InkView(Context context, HandwritingIme.HandwritingFinisher hwController,
             HandwritingIme.StylusConsumer consumer) {
         super(context);
@@ -66,6 +70,7 @@
 
         canvas.drawPath(mPath, mPaint);
         canvas.drawARGB(20, 255, 50, 50);
+        BoundsInfoDrawHelper.draw(canvas, this, mBoundsInfoMode, mCursorAnchorInfo);
     }
 
     private void stylusStart(float x, float y) {
@@ -156,4 +161,15 @@
         return mFinishRunnable;
     }
 
+    void setCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
+        mCursorAnchorInfo = cursorAnchorInfo;
+        invalidate();
+    }
+
+    void setBoundsInfoMode(int boundsInfoMode) {
+        if (boundsInfoMode != mBoundsInfoMode) {
+            invalidate();
+        }
+        mBoundsInfoMode = boundsInfoMode;
+    }
 }
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index a4c48fd..4fa6fbe 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -25,7 +25,7 @@
         "services.core.unboosted",
         "testables",
         "truth-prebuilt",
-        "ub-uiautomator",
+        "androidx.test.uiautomator_uiautomator",
     ],
     test_suites: ["device-tests"],
 }
diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt
index 0246426..d185ee6 100644
--- a/tests/Input/src/com/android/test/input/AnrTest.kt
+++ b/tests/Input/src/com/android/test/input/AnrTest.kt
@@ -27,14 +27,15 @@
 import android.os.SystemClock
 import android.provider.Settings
 import android.provider.Settings.Global.HIDE_ERROR_DIALOGS
-import android.support.test.uiautomator.By
-import android.support.test.uiautomator.UiDevice
-import android.support.test.uiautomator.UiObject2
-import android.support.test.uiautomator.Until
 import android.testing.PollingCheck
 import android.view.InputDevice
 import android.view.MotionEvent
 
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.Until
+
 import org.junit.After
 import org.junit.Assert.assertEquals
 import org.junit.Assert.assertTrue
diff --git a/tests/WindowAnimationJank/Android.bp b/tests/WindowAnimationJank/Android.bp
index ed86aa5..8542f88 100644
--- a/tests/WindowAnimationJank/Android.bp
+++ b/tests/WindowAnimationJank/Android.bp
@@ -25,7 +25,7 @@
     name: "WindowAnimationJank",
     srcs: ["src/**/*.java"],
     static_libs: [
-        "ub-uiautomator",
+        "androidx.test.uiautomator_uiautomator",
         "androidx.test.janktesthelper",
         "junit",
     ],
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java b/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java
index 2531464..48a359c 100644
--- a/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java
@@ -18,11 +18,12 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.os.SystemClock;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.BySelector;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
+
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
 
 /**
  * Set of helpers to manipulate test activities.
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java b/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java
index a8ace162..cb7c511 100644
--- a/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java
@@ -16,9 +16,8 @@
 
 package android.windowanimationjank;
 
-import android.support.test.uiautomator.UiDevice;
-
 import androidx.test.jank.JankTestBase;
+import androidx.test.uiautomator.UiDevice;
 
 /**
  * This adds additional system level jank monitor and its result is merged with primary monitor
diff --git a/tests/testables/src/android/testing/TestableSettingsProvider.java b/tests/testables/src/android/testing/TestableSettingsProvider.java
index c6f18fd..b850cb8 100644
--- a/tests/testables/src/android/testing/TestableSettingsProvider.java
+++ b/tests/testables/src/android/testing/TestableSettingsProvider.java
@@ -72,7 +72,11 @@
 
     public Bundle call(String method, String arg, Bundle extras) {
         // Methods are "GET_system", "GET_global", "PUT_secure", etc.
-        final int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, UserHandle.myUserId());
+        int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, UserHandle.USER_CURRENT);
+        if (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF) {
+            userId = UserHandle.myUserId();
+        }
+
         final String[] commands = method.split("_", 2);
         final String op = commands[0];
         final String table = commands[1];